使用简单的C语言示例进行HTTP POST并消耗响应

131

我想创建一个非常简单的C应用程序,执行HTTP POST操作。它将使用几个参数构建URL,我只需进行简单的HTTP POST并获取响应而不使用curl(此机器上未安装且不会安装curl库)。

伪代码:

  1. 处理2个参数

  2. 将args放入模板URL中:http://api.somesite.com/apikey=ARG1&command=ARG2

  3. 在生成的URL上执行POST操作

  4. 消费响应

我的Google和SO搜索没有找到有关此问题的任何信息。


3
你使用任何网络框架吗?你使用什么操作系统? - cnicutar
这只是一个基本的 Fedora 或 Cent 系统。网络框架通常是 sys/socket、netdb、arpa/inet,但不包括 libcurl。 - kmarks2
1
不是libcurl。您是否愿意使用其他库,或者必须全部使用POSIX? - cnicutar
所有的 POSIX 都不行,它必须在任何系统上都能够完全独立运行。 - kmarks2
3
我为你准备了一个样例,但我不明白为什么在消息体中没有内容时你要使用POST。如果所有参数都在查询字符串中,为什么不使用GET呢? - Jerry Jeremiah
显示剩余4条评论
4个回答

260

一条消息由头部和消息体组成,它们之间用一个空行分隔。即使没有消息体,空行也始终是必需的。头部以命令开头,并有多行键值对,用冒号和空格分隔。如果有消息体,它可以是任何你想要的内容。

头部中的行和头部末尾的空行必须以回车和换行符结尾(参见HTTP header line break style),这就是为什么这些行在末尾有\r\n的原因。

URL 的格式为 http://host:port/path?query_string

有两种主要的方法向网站提交请求:

  • GET: 查询字符串是可选的,但如果指定,则必须相对较短。因此,标头可以只是GET命令而没有其他内容。示例消息可能如下:

      GET /path?query_string HTTP/1.0\r\n
      \r\n
    
  • POST: 通常在查询字符串中的内容现在在消息体中。因此,标头需要包括Content-Type和Content-Length属性以及POST命令。示例消息可能如下:

      POST /path HTTP/1.0\r\n
      Content-Type: text/plain\r\n
      Content-Length: 12\r\n
      \r\n
      query_string
    
所以,回答你的问题:如果你想要POST的URL是http://api.somesite.com/apikey=ARG1&command=ARG2,那么它没有正文或查询字符串,因此没有理由进行POST,因为消息正文中没有任何内容可放置,也没有任何内容可以放置在Content-Type:和Content-Length:中。
我猜你如果真的想要POST,那么你的消息会像这样:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n
\r\n

因此,要发送消息,C程序需要执行以下操作:

  • 创建套接字
  • 查找IP地址
  • 打开套接字
  • 发送请求
  • 等待响应
  • 关闭套接字

发送和接收调用不一定会发送/接收您提供的所有数据 - 它们将返回实际发送/接收的字节数。 您需要在循环中调用它们并发送/接收其余消息。

在此示例中,我没有进行任何真正的错误检查 - 当发生错误时,我只是退出程序。 如果适合您,请告诉我:

#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#include <sys/socket.h> /* socket, connect */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#include <netdb.h> /* struct hostent, gethostbyname */

void error(const char *msg) { perror(msg); exit(0); }

int main(int argc,char *argv[])
{
    /* first what are we going to send and where are we going to send it? */
    int portno =        80;
    char *host =        "api.somesite.com";
    char *message_fmt = "POST /apikey=%s&command=%s HTTP/1.0\r\n\r\n";

    struct hostent *server;
    struct sockaddr_in serv_addr;
    int sockfd, bytes, sent, received, total;
    char message[1024],response[4096];

    if (argc < 3) { puts("Parameters: <apikey> <command>"); exit(0); }

    /* fill in the parameters */
    sprintf(message,message_fmt,argv[1],argv[2]);
    printf("Request:\n%s\n",message);

    /* create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) error("ERROR opening socket");

    /* lookup the ip address */
    server = gethostbyname(host);
    if (server == NULL) error("ERROR no such host");

    /* fill in the structure */
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(portno);
    memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);

    /* connect the socket */
    if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
        error("ERROR connecting");

    /* send the request */
    total = strlen(message);
    sent = 0;
    do {
        bytes = write(sockfd,message+sent,total-sent);
        if (bytes < 0)
            error("ERROR writing message to socket");
        if (bytes == 0)
            break;
        sent+=bytes;
    } while (sent < total);

    /* receive the response */
    memset(response,0,sizeof(response));
    total = sizeof(response)-1;
    received = 0;
    do {
        bytes = read(sockfd,response+received,total-received);
        if (bytes < 0)
            error("ERROR reading response from socket");
        if (bytes == 0)
            break;
        received+=bytes;
    } while (received < total);

    /*
     * if the number of received bytes is the total size of the
     * array then we have run out of space to store the response
     * and it hasn't all arrived yet - so that's a bad thing
     */
    if (received == total)
        error("ERROR storing complete response from socket");

    /* close the socket */
    close(sockfd);

    /* process response */
    printf("Response:\n%s\n",response);

    return 0;
}

正如其他答案所指出的那样,4096字节并不是一个非常大的响应。我随机选择了这个数字,假设您的请求的响应很短。如果它可以很大,您有两个选择:

  • 从响应中读取Content-Length:头,然后动态分配足够的内存来容纳整个响应。
  • 将响应作为片段到达时写入文件

回答评论中提出的问题的附加信息:

如果您想在消息正文中POST数据怎么办?那么您确实需要包括Content-Type:和Content-Length:头。Content-Length:是头与正文之间的空行后面的所有内容的实际长度。

以下是一个示例,它采用以下命令行参数:

  • 主机
  • 端口
  • 命令(GET或POST)
  • 路径(不包括查询数据)
  • 查询数据(放入GET的查询字符串和POST的正文中)
  • 标题列表(如果使用POST,则Content-Length:是自动的)

因此,对于最初的问题,您将运行:

a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"

针对评论中提出的问题,您需要运行以下代码:

a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"

这里是代码:

#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit, atoi, malloc, free */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#include <sys/socket.h> /* socket, connect */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#include <netdb.h> /* struct hostent, gethostbyname */

void error(const char *msg) { perror(msg); exit(0); }

int main(int argc,char *argv[])
{
    int i;
    
    /* first where are we going to send it? */
    int portno = atoi(argv[2])>0?atoi(argv[2]):80;
    char *host = strlen(argv[1])>0?argv[1]:"localhost";

    struct hostent *server;
    struct sockaddr_in serv_addr;
    int sockfd, bytes, sent, received, total, message_size;
    char *message, response[4096];

    if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); }

    /* How big is the message? */
    message_size=0;
    if(!strcmp(argv[3],"GET"))
    {
        message_size+=strlen("%s %s%s%s HTTP/1.0\r\n");        /* method         */
        message_size+=strlen(argv[3]);                         /* path           */
        message_size+=strlen(argv[4]);                         /* headers        */
        if(argc>5)
            message_size+=strlen(argv[5]);                     /* query string   */
        for(i=6;i<argc;i++)                                    /* headers        */
            message_size+=strlen(argv[i])+strlen("\r\n");
        message_size+=strlen("\r\n");                          /* blank line     */
    }
    else
    {
        message_size+=strlen("%s %s HTTP/1.0\r\n");
        message_size+=strlen(argv[3]);                         /* method         */
        message_size+=strlen(argv[4]);                         /* path           */
        for(i=6;i<argc;i++)                                    /* headers        */
            message_size+=strlen(argv[i])+strlen("\r\n");
        if(argc>5)
            message_size+=strlen("Content-Length: %lu\r\n")+20; /* content length */
        message_size+=strlen("\r\n");                          /* blank line     */
        if(argc>5)
            message_size+=strlen(argv[5]);                     /* body           */
    }
    
    /* allocate space for the message */
    message=malloc(message_size);
    
    /* fill in the parameters */
    if(!strcmp(argv[3],"GET"))
    {
        if(argc>5)
            sprintf(message,"%s %s%s%s HTTP/1.0\r\n",
                strlen(argv[3])>0?argv[3]:"GET",               /* method         */
                strlen(argv[4])>0?argv[4]:"/",                 /* path           */
                strlen(argv[5])>0?"?":"",                      /* ?              */
                strlen(argv[5])>0?argv[5]:"");                 /* query string   */
        else
            sprintf(message,"%s %s HTTP/1.0\r\n",
                strlen(argv[3])>0?argv[3]:"GET",               /* method         */
                strlen(argv[4])>0?argv[4]:"/");                /* path           */
        for(i=6;i<argc;i++)                                    /* headers        */
            {strcat(message,argv[i]);strcat(message,"\r\n");}
        strcat(message,"\r\n");                                /* blank line     */
    }
    else
    {
        sprintf(message,"%s %s HTTP/1.0\r\n",
            strlen(argv[3])>0?argv[3]:"POST",                  /* method         */
            strlen(argv[4])>0?argv[4]:"/");                    /* path           */
        for(i=6;i<argc;i++)                                    /* headers        */
            {strcat(message,argv[i]);strcat(message,"\r\n");}
        if(argc>5)
            sprintf(message+strlen(message),"Content-Length: %lu\r\n",strlen(argv[5]));
        strcat(message,"\r\n");                                /* blank line     */
        if(argc>5)
            strcat(message,argv[5]);                           /* body           */
    }

    /* What are we going to send? */
    printf("Request:\n%s\n",message);

    /* create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) error("ERROR opening socket");

    /* lookup the ip address */
    server = gethostbyname(host);
    if (server == NULL) error("ERROR no such host");

    /* fill in the structure */
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(portno);
    memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);

    /* connect the socket */
    if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
        error("ERROR connecting");

    /* send the request */
    total = strlen(message);
    sent = 0;
    do {
        bytes = write(sockfd,message+sent,total-sent);
        if (bytes < 0)
            error("ERROR writing message to socket");
        if (bytes == 0)
            break;
        sent+=bytes;
    } while (sent < total);

    /* receive the response */
    memset(response,0,sizeof(response));
    total = sizeof(response)-1;
    received = 0;
    do {
        bytes = read(sockfd,response+received,total-received);
        if (bytes < 0)
            error("ERROR reading response from socket");
        if (bytes == 0)
            break;
        received+=bytes;
    } while (received < total);

    /*
     * if the number of received bytes is the total size of the
     * array then we have run out of space to store the response
     * and it hasn't all arrived yet - so that's a bad thing
     */
    if (received == total)
        error("ERROR storing complete response from socket");

    /* close the socket */
    close(sockfd);

    /* process response */
    printf("Response:\n%s\n",response);

    free(message);
    return 0;
}

正如评论中所要求的那样,这是一个HTTP/1.1版本:

#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit, atoi, malloc, free */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#include <sys/socket.h> /* socket, connect */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#include <netdb.h> /* struct hostent, gethostbyname */

void error(const char *msg) { perror(msg); exit(0); }

int main(int argc,char *argv[])
{
    int i;
    
    /* first where are we going to send it? */
    int portno = atoi(argv[2])>0?atoi(argv[2]):80;
    char *host = strlen(argv[1])>0?argv[1]:"localhost";

    struct hostent *server;
    struct sockaddr_in serv_addr;
    int sockfd, bytes, sent, received, total, message_size;
    char *message, response[4096];

    if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); }

    /* How big is the message? */
    message_size=0;
    if(!strcmp(argv[3],"GET"))
    {
        message_size+=strlen("%s %s%s%s HTTP/1.1\r\n");           /* method            */
        message_size+=strlen(argv[3]);                            /* path              */
        message_size+=strlen(argv[4]);                            /* headers           */
        if(argc>5)
            message_size+=strlen(argv[5]);                        /* query string      */
        for(i=6;i<argc;i++)                                       /* headers           */
            message_size+=strlen(argv[i])+strlen("\r\n");
        message_size+=strlen("Host: %s\r\n");                     /* host header       */
        message_size+=strlen(argv[1]);                            /* host              */
        message_size+=strlen("Connection: close\r\n");            /* connection header */
        message_size+=strlen("\r\n");                             /* blank line        */
    }
    else
    {
        message_size+=strlen("%s %s HTTP/1.1\r\n");
        message_size+=strlen(argv[3]);                            /* method            */
        message_size+=strlen(argv[4]);                            /* path              */
        for(i=6;i<argc;i++)                                       /* headers           */
            message_size+=strlen(argv[i])+strlen("\r\n");
        message_size+=strlen("Host: %s\r\n");                     /* host header       */
        message_size+=strlen(argv[1]);                            /* host              */
        message_size+=strlen("Connection: close\r\n");            /* connection header */
        if(argc>5)
            message_size+=strlen("Content-Length: %lu\r\n")+20;   /* content length    */
        message_size+=strlen("\r\n");                             /* blank line        */
        if(argc>5)
            message_size+=strlen(argv[5]);                        /* body              */
    }
    
    /* allocate space for the message */
    message=malloc(message_size);
    
    /* fill in the parameters */
    if(!strcmp(argv[3],"GET"))
    {
        if(argc>5)
            sprintf(message,"%s %s%s%s HTTP/1.1\r\n",
                strlen(argv[3])>0?argv[3]:"GET",                  /* method            */
                strlen(argv[4])>0?argv[4]:"/",                    /* path              */
                strlen(argv[5])>0?"?":"",                         /* ?                 */
                strlen(argv[5])>0?argv[5]:"");                    /* query string      */
        else
            sprintf(message,"%s %s HTTP/1.1\r\n",
                strlen(argv[3])>0?argv[3]:"GET",                  /* method            */
                strlen(argv[4])>0?argv[4]:"/");                   /* path              */
        for(i=6;i<argc;i++)                                       /* headers           */
            {strcat(message,argv[i]);strcat(message,"\r\n");}
        sprintf(message+strlen(message),"Host: %s\r\n",           /* host header       */
            argv[1]);                                             /* host              */
        sprintf(message+strlen(message),"Connection: close\r\n"); /* connection header */
        strcat(message,"\r\n");                                   /* blank line        */
    }
    else
    {
        sprintf(message,"%s %s HTTP/1.1\r\n",
            strlen(argv[3])>0?argv[3]:"POST",                     /* method            */
            strlen(argv[4])>0?argv[4]:"/");                       /* path              */
        for(i=6;i<argc;i++)                                       /* headers           */
            {strcat(message,argv[i]);strcat(message,"\r\n");}
        sprintf(message+strlen(message),"Host: %s\r\n",           /* host header       */
            argv[1]);                                             /* host              */
        sprintf(message+strlen(message),"Connection: close\r\n"); /* connection header */
        if(argc>5)
            sprintf(message+strlen(message),"Content-Length: %lu\r\n",strlen(argv[5]));
        strcat(message,"\r\n");                                   /* blank line        */
        if(argc>5)
            strcat(message,argv[5]);                              /* body              */
    }

    /* What are we going to send? */
    printf("Request:\n%s\n",message);

    /* create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) error("ERROR opening socket");

    /* lookup the ip address */
    server = gethostbyname(host);
    if (server == NULL) error("ERROR no such host");

    /* fill in the structure */
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(portno);
    memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);

    /* connect the socket */
    if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
        error("ERROR connecting");

    /* send the request */
    total = strlen(message);
    sent = 0;
    do {
        bytes = write(sockfd,message+sent,total-sent);
        if (bytes < 0)
            error("ERROR writing message to socket");
        if (bytes == 0)
            break;
        sent+=bytes;
    } while (sent < total);

    /* receive the response */
    memset(response,0,sizeof(response));
    total = sizeof(response)-1;
    received = 0;
    do {
        bytes = read(sockfd,response+received,total-received);
        if (bytes < 0)
            error("ERROR reading response from socket");
        if (bytes == 0)
            break;
        received+=bytes;
    } while (received < total);

    /*
     * if the number of received bytes is the total size of the
     * array then we have run out of space to store the response
     * and it hasn't all arrived yet - so that's a bad thing
     */
    if (received == total)
        error("ERROR storing complete response from socket");

    /* close the socket */
    close(sockfd);

    /* process response */
    printf("Response:\n%s\n",response);

    free(message);
    return 0;
}

调用时应传递哪些参数? - Santiago Martí Olbrich
你需要将作为apikey使用的内容作为第一个参数传递,将作为命令使用的内容作为第二个参数传递。如果你想要使用完全不同的查询字符串,则需要更改格式字符串、参数数量和使用消息。 - Jerry Jeremiah
2
这段代码发出了格式不正确的HTTP请求。HTTP规定请求行必须以回车符/换行符对(\r\n)结尾,但是这段代码使用了裸露的换行符。 - John Bollinger
1
@JohnBollinger 非常正确。感谢您指出这一点。希望修改后的答案更好。 - Jerry Jeremiah
这个POST请求有什么问题?"POST /variableName=%s&value=%s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 4\r\n\r\n\r\n" 我想要像这样发帖:name=reaz。它响应了400错误的请求。 - Reaz Murshed
显示剩余11条评论

24

经过数周的研究,我得出了以下代码。我相信这是建立与Web服务器进行SSL安全连接所需的最少量。

#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>

#define APIKEY "YOUR_API_KEY"
#define HOST "YOUR_WEB_SERVER_URI"
#define PORT "443"

int main() {

    //
    //  Initialize the variables
    //
    BIO* bio;
    SSL* ssl;
    SSL_CTX* ctx;

    //
    //   Registers the SSL/TLS ciphers and digests.
    //
    //   Basically start the security layer.
    //
    SSL_library_init();

    //
    //  Creates a new SSL_CTX object as a framework to establish TLS/SSL
    //  or DTLS enabled connections
    //
    ctx = SSL_CTX_new(SSLv23_client_method());

    //
    //  -> Error check
    //
    if (ctx == NULL)
    {
        printf("Ctx is null\n");
    }

    //
    //   Creates a new BIO chain consisting of an SSL BIO
    //
    bio = BIO_new_ssl_connect(ctx);

    //
    //  Use the variable from the beginning of the file to create a 
    //  string that contains the URL to the site that you want to connect
    //  to while also specifying the port.
    //
    BIO_set_conn_hostname(bio, HOST ":" PORT);

    //
    //   Attempts to connect the supplied BIO
    //
    if(BIO_do_connect(bio) <= 0)
    {
        printf("Failed connection\n");
        return 1;
    }
    else
    {
        printf("Connected\n");
    }

    //
    //  The bare minimum to make a HTTP request.
    //
    char* write_buf = "POST / HTTP/1.1\r\n"
                      "Host: " HOST "\r\n"
                      "Authorization: Basic " APIKEY "\r\n"
                      "Connection: close\r\n"
                      "\r\n";

    //
    //   Attempts to write len bytes from buf to BIO
    //
    if(BIO_write(bio, write_buf, strlen(write_buf)) <= 0)
    {
        //
        //  Handle failed writes here
        //
        if(!BIO_should_retry(bio))
        {
            // Not worth implementing, but worth knowing.
        }

        //
        //  -> Let us know about the failed writes
        //
        printf("Failed write\n");
    }

    //
    //  Variables used to read the response from the server
    //
    int size;
    char buf[1024];

    //
    //  Read the response message
    //
    for(;;)
    {
        //
        //  Get chunks of the response 1023 at the time.
        //
        size = BIO_read(bio, buf, 1023);

        //
        //  If no more data, then exit the loop
        //
        if(size <= 0)
        {
            break;
        }

        //
        //  Terminate the string with a 0, to let know C when the string 
        //  ends.
        //
        buf[size] = 0;

        //
        //  ->  Print out the response
        //
        printf("%s", buf);
    }

    //
    //  Clean after ourselves
    //
    BIO_free_all(bio);
    SSL_CTX_free(ctx);

    return 0;
}

以上代码详细介绍了如何与远程服务器建立TLS连接。

重要提示:该代码没有检查公钥是否由有效机构签名。这意味着我没有使用根证书进行验证。请不要忘记实现此检查,否则您将不知道自己是否连接到正确的网站。

当涉及到请求本身时,它不过是手写HTTP请求。

您还可以在此链接下找到有关如何在系统中安装openSSL以及如何编译代码以使用安全库的说明。


3
好的解释! - Satyam Koyani

14

Jerry的回答很好。但是,它无法处理大型响应。为了解决这个问题,只需要进行一个简单的更改:

memset(response, 0, sizeof(response));
total = sizeof(response)-1;
received = 0;
do {
    printf("RESPONSE: %s\n", response);
    // HANDLE RESPONSE CHUCK HERE BY, FOR EXAMPLE, SAVING TO A FILE.
    memset(response, 0, sizeof(response));
    bytes = recv(sockfd, response, 1024, 0);
    if (bytes < 0)
        printf("ERROR reading response from socket");
    if (bytes == 0)
        break;
    received+=bytes;
} while (1); 

3
你可以把我的例子中的响应数组增大一些。我以为他只是得到一些 JSON 数据,而不是下载一个巨大的文件,但是当然,根据查询,即使 JSON 文件也可能有几兆字节... - Jerry Jeremiah
1
我是一名C语言初学者,您的答案可能是正确的。但是,您能否请在答案中加入解释? - boop
2
这只是对已接受答案的评论,不应该作为单独的回答尝试。 - Michael Gaskill
1
这里只有一件事要补充,这个方法很好用,但是你应该读取缓冲区大小减1字节。为了正确查看它,我不会在打印语句中使用换行符。应该像这样:bytes = recv(sockfd, response, 1023, 0) - xjsc16x
这里接收到的数据将是原始响应,我们如何仅解析消息/内容? - LIJIN T V

6

处理已添加。
已添加主机头。
添加了 Linux / Windows 支持,并进行了测试(XP、WIN7)。
警告:如果没有主机、路径或端口作为参数,则会出现“分段错误”。

#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit, atoi, malloc, free */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#ifdef __linux__ 
    #include <sys/socket.h> /* socket, connect */
    #include <netdb.h> /* struct hostent, gethostbyname */
    #include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#elif _WIN32
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <windows.h>
    #pragma comment(lib,"ws2_32.lib") //Winsock Library

#else

#endif

void error(const char *msg) { perror(msg); exit(0); }

int main(int argc,char *argv[])
{

    int i;
    struct hostent *server;
    struct sockaddr_in serv_addr;
    int bytes, sent, received, total, message_size;
    char *message, response[4096];
    int portno = atoi(argv[2])>0?atoi(argv[2]):80;
    char *host = strlen(argv[1])>0?argv[1]:"localhost";
    char *path = strlen(argv[4])>0?argv[4]:"/";
    if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); }
    /* How big is the message? */
    message_size=0;
    if(!strcmp(argv[3],"GET"))
    {
                printf("Process 1\n");
        message_size+=strlen("%s %s%s%s HTTP/1.0\r\nHost: %s\r\n");        /* method         */
        message_size+=strlen(argv[3]);                         /* path           */
        message_size+=strlen(path);                         /* headers        */
        if(argc>5)
            message_size+=strlen(argv[5]);                     /* query string   */
        for(i=6;i<argc;i++)                                    /* headers        */
            message_size+=strlen(argv[i])+strlen("\r\n");
        message_size+=strlen("\r\n");                          /* blank line     */
    }
    else
    {
                printf("Process 2\n");
        message_size+=strlen("%s %s HTTP/1.0\r\nHost: %s\r\n");
        message_size+=strlen(argv[3]);                         /* method         */
        message_size+=strlen(path);                            /* path           */
        for(i=6;i<argc;i++)                                    /* headers        */
            message_size+=strlen(argv[i])+strlen("\r\n");
        if(argc>5)
            message_size+=strlen("Content-Length: %d\r\n")+10; /* content length */
        message_size+=strlen("\r\n");                          /* blank line     */
        if(argc>5)
            message_size+=strlen(argv[5]);                     /* body           */
    }
            printf("Allocating...\n");
    /* allocate space for the message */
    message=malloc(message_size);

    /* fill in the parameters */
    if(!strcmp(argv[3],"GET"))
    {
        if(argc>5)
            sprintf(message,"%s %s%s%s HTTP/1.0\r\nHost: %s\r\n",
                strlen(argv[3])>0?argv[3]:"GET",               /* method         */
                path,                                          /* path           */
                strlen(argv[5])>0?"?":"",                      /* ?              */
                strlen(argv[5])>0?argv[5]:"",host);            /* query string   */
        else
            sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n",
                strlen(argv[3])>0?argv[3]:"GET",               /* method         */
                path,host);                                    /* path           */
        for(i=6;i<argc;i++)                                    /* headers        */
            {strcat(message,argv[i]);strcat(message,"\r\n");}
        strcat(message,"\r\n");                                /* blank line     */
    }
    else
    {
        sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n",
            strlen(argv[3])>0?argv[3]:"POST",                  /* method         */
            path,host);                                        /* path           */
        for(i=6;i<argc;i++)                                    /* headers        */
            {strcat(message,argv[i]);strcat(message,"\r\n");}
        if(argc>5)
            sprintf(message+strlen(message),"Content-Length: %d\r\n",(int)strlen(argv[5]));
        strcat(message,"\r\n");                                /* blank line     */
        if(argc>5)
            strcat(message,argv[5]);                           /* body           */
    }
    printf("Processed\n");
    /* What are we going to send? */
    printf("Request:\n%s\n",message);
        /* lookup the ip address */

    total = strlen(message);
    /* create the socket */
    #ifdef _WIN32
WSADATA wsa;
SOCKET s;

printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
    printf("Failed. Error Code : %d",WSAGetLastError());
    return 1;
}

printf("Initialised.\n");

//Create a socket
if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
{
    printf("Could not create socket : %d" , WSAGetLastError());
}

printf("Socket created.\n");

server = gethostbyname(host);
serv_addr.sin_addr.s_addr = inet_addr(server->h_addr);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
//Connect to remote server
if (connect(s , (struct sockaddr *)&serv_addr , sizeof(serv_addr)) < 0)
{
    printf("connect failed with error code : %d" , WSAGetLastError());
    return 1;
}

puts("Connected");
if( send(s , message , strlen(message) , 0) < 0)
{
    printf("Send failed with error code : %d" , WSAGetLastError());
    return 1;
}
puts("Data Send\n");

//Receive a reply from the server
if((received = recv(s , response , 2000 , 0)) == SOCKET_ERROR)
{
    printf("recv failed with error code : %d" , WSAGetLastError());
}

puts("Reply received\n");

//Add a NULL terminating character to make it a proper string before printing
response[received] = '\0';
puts(response);

closesocket(s);
WSACleanup();
    #endif
    #ifdef __linux__ 
    int sockfd;
    server = gethostbyname(host);
    if (server == NULL) error("ERROR, no such host");
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) error("ERROR opening socket");
        /* fill in the structure */
        memset(&serv_addr,0,sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(portno);
        memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
                /* connect the socket */
        if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
            error("ERROR connecting");
                /* send the request */

    sent = 0;
    do {
        bytes = write(sockfd,message+sent,total-sent);
        if (bytes < 0)
            error("ERROR writing message to socket");
        if (bytes == 0)
            break;
        sent+=bytes;
    } while (sent < total);
    /* receive the response */
    memset(response, 0, sizeof(response));
    total = sizeof(response)-1;
    received = 0;
    printf("Response: \n");
    do {
       printf("%s", response);
       memset(response, 0, sizeof(response));
       bytes = recv(sockfd, response, 1024, 0);
        if (bytes < 0)
           printf("ERROR reading response from socket");
       if (bytes == 0)
           break;
       received+=bytes;
    } while (1);

    if (received == total)
        error("ERROR storing complete response from socket");

    /* close the socket */
    close(sockfd);
    #endif


    free(message);

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接