• linux c使用socket进行http 通信,并接收任意大小的http响应(四)


    终于说到SOCKET 这里了。SOCKET进行http通信的实际就是利用socket将http请求信息发送给http服务器,然后再利用socket接收http响应。

    由于本文与之通信的服务器是ip已知的,所以为了能够将能够和互联网网站进行http通信还要另外像办法。

    代码如下:

    (1)http.h

    //http.c当中可能被其他程序锁用到的函数的声明
    #include "http_url.h"
    #ifndef http_h
    #define http_h
    typedef struct sockaddr_in ip;
    typedef struct sockaddr *IP;
    extern char cookie[200];
    extern int create_socket();
    extern void init_ip(ip *httpip,char *Ip,int port);
    extern int Bind(int socket,ip *httpip);
    extern int get_other_socket(int socket);
    extern int Connect(int socket,ip* httpip);
    extern int Send(int socket,char *Data);
    extern char* Read(int socket);
    extern int http_perform_url(PURL url,char **Buffer);
    extern int find_cookie(char *Cookie,char *httpBuffer);
    extern void randomCode(char *result);
    extern void Set_Cookie(char *cookie_);
    extern void httpBuffer_free(char **respond);
    #endif // http_h

    (2)http.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <pthread.h>
    #include "http_url.h"//加入http_hrl模块
    #include "data2.h"//加入data2模块,data2模块用于存储和处理HTTP响应
    #define SETCOOKIE "Set-Cookie: "
    #define SENDSIZE 4096//每次发送数据的大小,注:未使用

    #define http_h
    //注意:本文件所有定义函数只适合HTTP通信----排除某些例外函数
    //用sockaddr_in、struct sockaddr *结构体存储IP
    typedef struct sockaddr_in ip;
    typedef struct sockaddr *IP;

    //全局变量cookie,注:目前仅支持一个cookie
    char cookie[200];

    int is_respond_ok(char *httpBuffer);

    //自定义创建socket的函数
    //返回值:成功返回socket嵌套字,失败返回-1
    int create_socket()
    {
        int temp;
        temp=socket(AF_INET,SOCK_STREAM,0);
        return temp;

    }

    //自定义初始化ip结构体的函数
    //参数一:指向将被初始化的ip结构体的指针
    //参数二:具体的IP,例如:192.167.6.5
    //参数三:通信端口,HTTP通信应传入80
    void init_ip(ip *httpip,char *Ip,int port)
    {
        memset(httpip,0,sizeof(*httpip));
        httpip->sin_family=AF_INET;
        httpip->sin_addr.s_addr=inet_addr(Ip);
        httpip->sin_port=htons(port);
    }

    //自定义将socket和IP绑定的函数
    //参数一:socket嵌套字
    //参数二:已经指向已经初始化了的ip结构体的指针
    //返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中
    int Bind(int socket,ip *httpip)
    {
        int ret;
        ret=bind(socket,(IP)httpip,sizeof(httpip));
        return ret;
    }

    //自定义通过accept阻塞等待其他socket对其进行连接的函数
    //参数一:要被阻塞的socket嵌套字
    //返回值:有其他socket连接到被阻塞的socket则返回和被阻塞的socket进行通信的另一socket嵌套字
    int get_other_socket(int socket)
    {
        ip otherip;
        int temp;
        socklen_t length;
        length=sizeof(otherip);
        temp=accept(socket,(IP)&otherip,&length);
        return temp;

    }


    //自定义connect函数
    //参数一:客户端自己的socket
    //参数二:指向将被连接的IP结构体的指针
    //返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中
    int Connect(int socket,ip* httpip)
    {
        int flag;
        flag=connect(socket,(IP)httpip,sizeof(*httpip));
        return flag;
    }


    //自定义Send函数
    //参数一:自己程序的socket
    //参数二:将要发送的数据
    //返回值:大于或等于零表示发送了部分或是全部的数据,失败返回-1, 错误原因存于errno 中
    //待修改:由于write函数一次可能写不完,所以可能需要修改函数进行多次发送???????????????????如果我没有修改,请后来的勇者继续修改
    int Send(int socket,char *Data)
    {
        int ret=1;
    /*    int sendLength=0,dataLength=0;

        while(ret!=0)
        {}
    */

        ret=write(socket,Data,strlen(Data));
        return ret;
    }

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


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

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

         //每次读取1024个节点存储到buffer中,然后再通过strncpy复制到BUFFER节点当中
        while((return_length=read(socket,buffer,1024))>0)
        {
            if(return_length==-1)
            {
                printf(" receive wrong! ");
                free_Buffer_Link(header);
                header=NULL;
                return NULL;
            }else
            {

                if(length>=50176)//如果节点已经快要存满,则新建节点,将相应内容存到新建立的节点当中
                {
                    nowBuffer->data[length]='';
                    if(NULL==(nowBuffer=append_Buffer_Node(header)))
                    {
                        printf(" append_Buffer_Node() fail in http.c Read() ");//节点添加失败直接返回
                        free_Buffer_Link(header);
                        return NULL;
                    }
                    length=0;
                    strncpy(nowBuffer->data+length,buffer,return_length);
                    length+=return_length;
                }
                else
                {
                    strncpy(nowBuffer->data+length,buffer,return_length);
                    length+=return_length;
                }
            }

        }

        nowBuffer->data[length]='';
        Data=get_All_Buffer(header);//将BUFFER链表中的内容取出,存储到动态内存当中

         //释放BUFFER链表
         if(header!=NULL)
        {
            free_Buffer_Link(header);
            header=NULL;
        }

        if(length==0)
        {
           printf("no date receive! ");
            return NULL;
        }

        return Data;//返回指向存储有响应内容的动态内存的指针(可能为空)
    }

    //进行HTTP通信
    //参数一:要进行HTTP通信的IP,如192.168.6.1
    //参数二:发送给HTTP服务端的数据
    //参数三:指向存储HTTP响应内容指针的变量的指针,如不明白,可参考动态内存在函数间的传递
    //返回值:成功返回1,失败返回0
    //注意:动态内存必须释放
    int http_perform(char *Ip,char *DataSend,char **buffer)
    {

        int client_socket_word;
        ip httpip;

        if(-1==(client_socket_word=create_socket()))
        {
            perror("http.c create_socket() fail");
            return 0;
        }


        init_ip(&httpip,Ip,80);

        if(-1==Connect(client_socket_word,&httpip))
        {
            perror("http.c Connect() fail");
            return 0;
        }


        if(-1==Send(client_socket_word,DataSend))
        {
            perror("http.c Send() fail");
            return 0;
        }


        *buffer=Read(client_socket_word);

        if(NULL==buffer)
        {
            perror("http.c Read() fail");
            return 0;
        }

        if(-1!=client_socket_word)
        {
            close(client_socket_word);
            return 1;
        }

        return 0;
    }

    //通过结构体URL进行HTTP通信
    //参数一:存储有通信信息的结构体
    //参数二:指向存储HTTP响应的动态指针的地址,使用完之后必须释放,使用之前必须检查BUFFER是否为NULL(即使is_respond_ok()失败,动态内存中依旧可能存储有响应失败的信息)
    //返回值:成功返回1,失败返回0
    //注意:无论成功或是失败,动态内存真的必须释放!!!
    int http_perform_url(PURL url,char **Buffer)
    {
        char host[100];
        char path[500];
        char request[10*1024];//差点忘了你

        memset(request,'',10*1024*sizeof(char));

        if(1==http_request(request,url))
        {
            if(1==parse_url(host,path,url->url))
            {

                if(1==http_perform(host,request,Buffer))
                {
                    if(NULL!=Buffer)
                    {
                        if(1==is_respond_ok(*Buffer))
                        {
                            return 1;
                        }
                    }
                }


            }
        }

        return 0;
    }

    //截取两个指针之间的字符串
    //参数一:字符串开始的指针
    //参数二:字符串结束的指针
    //返回值:成功返回1,失败返回0
    int str_between(char* result,char *pStart,char *pEnd)
    {
        char *result_,*pStart_;
        result_=result;
        pStart_=pStart;

        if((pStart)&&(pEnd)&&(pEnd>pStart))
        {
            while((pStart_)!=pEnd)
            {
                *(result_)=*(pStart_);
                pStart_++;
                result_++;

            }
        }else
        {
            return 0;
        }
        *(result_)='';
        return 1;
    }

    //从HTTP响应当中寻找COOKIE
    //参数一:存储COOKIE字符串的指针
    //参数二:存储有COOKIE的HTTPBUFFER
    int find_cookie(char *Cookie,char *httpBuffer)
    {
        char *pStart,*pEnd,*temp;
        if((strstr(httpBuffer,SETCOOKIE)!=NULL))
        {
            temp=strstr(httpBuffer,SETCOOKIE);
            pStart=temp+sizeof(SETCOOKIE);
            pStart--;
        }

        if((temp=strstr(httpBuffer,"; path"))!=NULL)
        {
            pEnd=temp;
        }

        if(!((pStart)&&(pEnd)))
        {
            return 0;
        }

        if(str_between(Cookie,pStart,pEnd))
        {
            printf("%s ",Cookie);
            return 1;
        }
        else
        {
            return 0;
        }

    }

    //当想要自己设置cookie,可以通过这个函数将自定义的cookie复制到全局变量当中
    //参数一:cookie字符串,如:“synsnd=hdh38rhy3gdyug873gdyguwsgdipo39yrdh”
    void Set_Cookie(char *cookie_)
    {
        if(cookie_)
        {
            strcpy(cookie,cookie_);
        }

    }

    //通过检查HTTPBUFFER当中是否有“302 Found”和“200 OK”判断响应是否成功
    //参数1:HTTPBUFFER
    //返回值:成功返回1,失败返回0
    int is_respond_ok(char *httpBuffer)
    {
        if((strstr(httpBuffer,"302 Found"))||(strstr(httpBuffer,"200 OK")))
        {
            printf("Respond ok! ");
            return 1;
        }
        else
        {
            printf("Respond wrong! ");
            return 0;

        }
    }

    //产生长度大概为16-19的随机数字字符串
    //参数一:用于存储随机数字字符串的字符串指针
    //待修改:我没有设定返回值和纠错机制,只要用足够大的字符串去接受的话,一般不会出错
    void randomCode(char *result)
    {
        int i;
        char temp1[20],temp2[20];
        srand((unsigned int)time(NULL));
        i=rand();
        num_to_string(temp1,i);
        i=rand();
        num_to_string(temp2,i);

        strcpy(result,temp1);
        strcat(result,temp2);
    }

    //当HTTP响应不为空,用以释放存储响应的动态内存
    void httpBuffer_free(char **respond)
    {
        if(*respond!=NULL)
        {
            free(*respond);
            *respond=NULL;//应该为*respond=NULL而不是respond=NULL,改变的是respond中存储的指针
        }
    }

    下一篇放使用例子。

  • 相关阅读:
    Systemd程序及相关命令
    深入理解SELinux
    Linux系统常见内核问题修复(转发)
    CentOS6.8单用户模式下修改密码
    CentOS6启动流程
    linux中的软、硬链接
    Linux中计划任务、周期性任务设置
    CentOS7.3将网卡命名方式设置为传统方式
    js判断字符串是否有下划线
    判断是否是微信打开
  • 原文地址:https://www.cnblogs.com/thegodofthunder/p/7227126.html
Copyright © 2020-2023  润新知