执行 HTTP POST 并使用响应的简单 C 示例 [英] Simple C example of doing an HTTP POST and consuming the response

查看:34
本文介绍了执行 HTTP POST 并使用响应的简单 C 示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个非常简单的 C 应用程序来执行 HTTP 发布.它将需要一些参数,并使用这些参数来构造一个 URL.我只想做一个简单的 HTTP POST 并在不使用 curl 的情况下获得响应(库没有也不会安装在需要运行的机器上).

I would like to create a very simple C application that does an HTTP post. It will take a few parameters, and use these to construct a URL. I'd just like to do a simple HTTP POST and get the response without the use of curl (the libraries are not and will not be installed on the machine this needs to run).

伪代码:

  1. 处理 2 个参数

  1. Process 2 args

将参数放入模板 URL:http://api.somesite.com/apikey=ARG1&命令=ARG2

Put args into template URL: http://api.somesite.com/apikey=ARG1&command=ARG2

对生成的 URL 执行 POST

Do POST on generated URL

使用响应

我的 Google 和 SO 搜索在这个问题上没有产生任何结果.

My Google and SO searches haven't yielded anything on this matter.

推荐答案

消息具有由空行分隔的标题部分和消息正文.即使没有消息正文,也始终需要空行.标题以命令开头,并具有由冒号和空格分隔的附加键值对行.如果有消息正文,它可以是您想要的任何内容.

A message has a header part and a message body separated by a blank line. The blank line is ALWAYS needed even if there is no message body. The header starts with a command and has additional lines of key value pairs separated by a colon and a space. If there is a message body, it can be anything you want it to be.

标题中的行和标题末尾的空行必须以回车换行对结束(请参阅HTTP 标头换行样式) 所以这就是为什么这些行在末尾有 的原因.

Lines in the header and the blank line at the end of the header must end with a carraige return and linefeed pair (see HTTP header line break style) so that's why those lines have at the end.

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

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

There are two main ways of submitting a request to a website:

  • GET:查询字符串是可选的,但如果指定,则必须相当短.因此,标头可能只是 GET 命令而没有别的.示例消息可以是:

  • GET: The query string is optional but, if specified, must be reasonably short. Because of this the header could just be the GET command and nothing else. A sample message could be:

  GET /path?query_string HTTP/1.0

  

  • POST:通常在查询字符串中的内容是在消息正文中.因此,标头需要包含 Content-Type: 和 Content-Length: 属性以及 POST 命令.示例消息可以是:

  • POST: What would normally be in the query string is in the body of the message instead. Because of this the header needs to include the Content-Type: and Content-Length: attributes as well as the POST command. A sample message could be:

      POST /path HTTP/1.0
    
      Content-Type: text/plain
    
      Content-Length: 12
    
      
    
      query_string
    

  • 所以,回答您的问题:如果您有兴趣发布的 URL 是 http://api.somesite.com/apikey=ARG1&command=ARG2 然后没有正文或查询字符串,因此没有理由 POST 因为没有任何东西可以放入消息正文等在 Content-Type: 和 Content-Length: 中没有什么可放的:

    So, to answer your question: if the URL you are interested in POSTing to is http://api.somesite.com/apikey=ARG1&command=ARG2 then there is no body or query string and, consequently, no reason to POST because there is nothing to put in the body of the message and so nothing to put in the Content-Type: and Content-Length:

    我想如果你真的愿意,你可以发布.在这种情况下,您的消息将如下所示:

    I guess you could POST if you really wanted to. In that case your message would look like:

    POST /apikey=ARG1&command=ARG2 HTTP/1.0
    
    
    
    

    所以要发送 C 程序需要的消息:

    So to send the message the C program needs to:

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

    发送和接收调用不一定会发送/接收您提供给它们的所有数据——它们将返回实际发送/接收的字节数.您可以在循环中调用它们并发送/接收消息的其余部分.

    The send and receive calls won't necessarily send/receive ALL the data you give them - they will return the number of bytes actually sent/received. It is up to you to call them in a loop and send/receive the remainder of the message.

    我在这个示例中没有做的是任何类型的真正错误检查 - 当出现问题时我只是退出程序.让我知道它是否适合您:

    What I did not do in this sample is any sort of real error checking - when something fails I just exit the program. Let me know if it works for you:

    #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
    
    ";
    
        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:
    %s
    ",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:
    %s
    ",response);
    
        return 0;
    }
    

    就像其他答案指出的那样,4096 字节并不是一个很大的响应.假设对您的请求的响应很短,我随机选择了该数字.如果它可以很大,你有两个选择:

    Like the other answer pointed out, 4096 bytes is not a very big response. I picked that number at random assuming that the response to your request would be short. If it can be big you have two choices:

    • 从响应中读取 Content-Length: 标头,然后动态分配足够的内存来保存整个响应.
    • 在碎片到达时将响应写入文件

    回答评论中提出的问题的其他信息:

    Additional information to answer the question asked in the comments:

    如果您想在消息正文中 POST 数据怎么办?然后您确实需要包含 Content-Type: 和 Content-Length: 标头.内容长度:是将标题与正文分开的空行之后的所有内容的实际长度.

    What if you want to POST data in the body of the message? Then you do need to include the Content-Type: and Content-Length: headers. The Content-Length: is the actual length of everything after the blank line that separates the header from the body.

    这是一个采用以下命令行参数的示例:

    Here is a sample that takes the following command line arguments:

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

    因此,对于您将运行的原始问题:

    So, for the original question you would run:

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

    对于评论中提出的问题,您将运行:

    And for the question asked in the comments you would run:

    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
    ");        /* 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("
    ");
            message_size+=strlen("
    ");                          /* blank line     */
        }
        else
        {
            message_size+=strlen("%s %s HTTP/1.0
    ");
            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("
    ");
            if(argc>5)
                message_size+=strlen("Content-Length: %d
    ")+10; /* content length */
            message_size+=strlen("
    ");                          /* 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
    ",
                    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
    ",
                    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,"
    ");}
            strcat(message,"
    ");                                /* blank line     */
        }
        else
        {
            sprintf(message,"%s %s HTTP/1.0
    ",
                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,"
    ");}
            if(argc>5)
                sprintf(message+strlen(message),"Content-Length: %d
    ",strlen(argv[5]));
            strcat(message,"
    ");                                /* blank line     */
            if(argc>5)
                strcat(message,argv[5]);                           /* body           */
        }
    
        /* What are we going to send? */
        printf("Request:
    %s
    ",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:
    %s
    ",response);
    
        free(message);
        return 0;
    }
    

    这篇关于执行 HTTP POST 并使用响应的简单 C 示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆