c - 执行 HTTP POST 和使用响应的简单 C 示例

标签 c http http-post

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

伪代码:

  1. 处理 2 个参数

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

  3. 对生成的 URL 执行 POST

  4. 消费响应

我的 Google 和 SO 搜索在这件事上没有产生任何结果。

最佳答案

消息有一个标题部分和一个空行分隔的消息正文。即使没有消息正文,也总是需要空行。 header 以命令开头,并有额外的键值对行,由冒号和空格分隔。如果有消息正文,它可以是您想要的任何内容。

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

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

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

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

      GET /path?query_string HTTP/1.0\r\n
      \r\n
    
  • POST:通常在查询字符串中的内容在消息正文中。因此, header 需要包含 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
    

因此,回答您的问题:如果您有兴趣发布到的 URL 是 http://api.somesite.com/apikey=ARG1&command=ARG2那么就没有正文或查询字符串,因此没有理由进行 POST,因为没有任何内容可放入消息正文中,因此也没有任何内容可放入 Content-Type: 和 Content-Length:

我想如果你真的想的话,你可以发帖。在这种情况下,您的消息将如下所示:

POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n
\r\n

因此,要发送 C 程序需要的消息:

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

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

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

#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: header ,然后动态分配足够的内存来容纳整个响应。
  • 在作品到达时将响应写入文件

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

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

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

  • 主持人
  • 港口
  • 命令(GET 或 POST)
  • 路径(不包括查询数据)
  • 查询数据(GET 放入查询字符串,POST 放入正文)
  • header 列表(Content-Length:如果使用 POST,则自动生成)

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

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: %d\r\n")+10; /* 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: %d\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;
}

关于c - 执行 HTTP POST 和使用响应的简单 C 示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22077802/

相关文章:

rest - Backbone Send Post 数据编码为查询字符串

c# - 使用 C# 和华为 E8372 加密狗发送短信

android - 将图像上传到android中的服务器?

c - 如何使用 malloc 捕获程序员错误?

c++ - eclipse CDT : doesn't show name a macro appears in

javascript - 在错误超时之前重新发送 HTTP post

java - 创建用于测试分布式系统的网络错误

c - "On demand"服务器中全局结构的共享内存段

c - Unix:在C中复制具有原始权限的文件

http - 如何从 shell 脚本中使用 pastebin?