C语言 使用socket发送http请求接收任意大小响应内容

最近想使用JNI完成http请求,由于之前有C语言基础,就来了兴致研究了一番。

请务必理解http的传输层数据格式,\r\n和\r\n\r\n,例:

#define HTTP_POST "POST /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n"\
    "Content-Type:application/x-www-form-urlencoded\r\nContent-Length: %zu%s\r\n\r\n%s"
#define HTTP_GET "GET /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*%s\r\n\r\n"

 

提供给各位完成http请求的两种方案:

  1. 使用自带的socket.h完成http请求;
  2. 使用curl网络库完成http请求;

我使用了第一种,网上有几位的代码我参考了一下,基本上可以实现http网络请求,不过都会存在一些问题:

  1. socket read/rev阻塞,每次读取1024字节,循环读取,打印出来数据已经接收完成,但循环依旧阻塞在read/rev方法那里,直到服务器端超时关闭socket,才会执行完。我看了其他的帖子:①有说用非阻塞的,但是非阻塞不适合http请求的场景,服务器不会一直返回数据,不需要while死循环读取;②有说判断read/rev状态的,但阻塞就是阻塞,结束才有状态,所以也不可行。我的实现思路是:读取http的content-length数据长度,然后每次循环截取/r/n/r/n后面的数据,对比两个长度,如果一样,则证明数据接收完成,直接返回,关闭socket。
  2. http数据大小是不能确定的,有可能是任意大小,所以使用了buffer保存,最后在转换为char*数组

关键代码(读取http响应内容):

//自定义Read函数
//参数一:自己程序的socket
//返回值:成功返回指向存储有相应内容的动态内存的指针,失败返回NULL
//注意:1)返回的动态内存指针在不使用的时候应该通过free释放;2)如果不是真的有问题,请不要动,如果读取数据出现问题,请优先检查data2.c中的函数
char *Read(int socket) {
    int length = 0, return_length;
    char buffer[BUFFER_SIZE];
    char *Data;
    PBUFFER header, nowBuffer;//nowBuffer指向正在使用的BUFFER节点


    if (NULL != (header = create_EmptyBufferLink()))//创建BUFFER表头
    {
        if (NULL == (nowBuffer = append_Buffer_Node(header)))//创建第一个存储响应的BUFFER节点
        {
            LOGD("\nappend_Buffer_Node() fail in http.c Read()\n");//节点添加失败直接返回
            free_Buffer_Link(header);
            return NULL;
        }

    } else {
        LOGD("\ncreate_EmptyBufferLink() fail in http.c Read()\n");//头结点创建失败直接返回
        return NULL;
    }

    int content_length = -1,header_length=-1;

    //每次读取1024个节点存储到buffer中,然后再通过strncpy复制到BUFFER节点当中
    while ((return_length = recv(socket, buffer, BUFFER_SIZE,0)) > 0) {
        if (return_length == -1) {
            LOGD("\nreceive wrong!\n");
            free_Buffer_Link(header);
            header = NULL;
            break;
        } else {
            if (length >= 50176)//如果节点已经快要存满,则新建节点,将相应内容存到新建立的节点当中
            {
                nowBuffer->data[length] = '\0';
                if (NULL == (nowBuffer = append_Buffer_Node(header))) {
                    LOGD("\nappend_Buffer_Node() fail in http.c Read()\n");//节点添加失败直接返回
                    free_Buffer_Link(header);
                    break;
                }
                length = 0;
                strncpy(nowBuffer->data + length, buffer, return_length);
                length += return_length;
            } else {
                strncpy(nowBuffer->data + length, buffer, return_length);
                length += return_length;
            }
            //得到content-length所在位置,通过atoi函数得到请求数据的长度
            if (content_length==-1){
                char *ptmp = (char *) strstr(buffer, "Content-Length");
                if (!ptmp) {
                    LOGD("本次数据不包含Conteng-length");
                }
                content_length = atoi(ptmp + strlen("Content-Length: "));
                LOGD("Conteng-length数据长度为:%d,指针ptmp:%d", content_length,ptmp);
            }
            nowBuffer->data[length] = '\0';
            Data = get_All_Buffer(header);
            LOGD("哈哈:%s",Data);
            char *p = strstr(Data,"\r\n\r\n");
            int now_length = strlen(Data) - (p-Data+4);
            LOGD("测试指针下标位置为:%d;数据位置为:%d;实际数据长度为:%d",p-Data+1,p-Data+4,now_length);
            //通过得到\r\n\r\n的位置,计算响应内容的长度。
            // 如果计算得到的长度和content-length相等,则认为http请求结束。
            if (now_length >= content_length){
                //释放BUFFER链表
                if (header != NULL) {
                    free_Buffer_Link(header);
                    header = NULL;
                }
                //申请响应内容的内存,用于存放数据。
                char *response = (char *) malloc(now_length+1);
                if (!response) {
                    LOGD("malloc failed \n");
                    return NULL;
                }
                LOGD("终于该拷贝了...,指针p:%d",p);
                strcpy(response, p + 4);
                return response;
            }
        }
    }

    //释放BUFFER链表
    if (header != NULL) {
        free_Buffer_Link(header);
        header = NULL;
    }
    return NULL;//返回指向存储有响应内容的动态内存的指针(可能为空)
}

 

http代码参见:https://github.com/VcStrong/CHttpDemo/tree/master/common/src/main/cpp

你可能感兴趣的:(C语言)