HTTP之ARM编程(在imx6ul上实现http协议通讯)

 首先,简单介绍基于HTTP协议的客户/服务器模式的信息交换过程,它分四个过程(建发响关),建立连接、发送请求信息、发送响应信息、关闭连接;在WWW中,“客户”与“服务器”是一个相对的概念,只存在于一个特定的连接期间,即在某个连接中的客户在另一个连接中可能作为服务器。WWW服务器运行时,一直在TCP 80端口(WWW的缺省端口)监听-listen,等待连接的出现。

其次。http是基于tcp的协议(tcp是传输层协议-osi第四层,http是osi第7层),所以具备tcp协议的特征,为了方便理解,查看之前文章https://blog.csdn.net/xiaoxilang/article/details/80839797;故而:
tcp的服务端(sbla(撕逼啦) rwrc)和客户端(sc wrc)的代码过程:

常常结合json 格式进行传输https://mp.csdn.net/editor/html/105176546

1 服务器端:代码如下:sbla如此:

pthread_create-->processthread()-->GET_COMMON类-->processgetcommon()解析函数-->传输文件函数transferfile() 应答-->传输文件,是调用sendsegment()函数来实现-->就是用的socket里面的send()函数

void* server_process(void *p)
{
    int serverSocket; //**** 1-s-socket初始化-创建一个套接字 ****
    struct sockaddr_in server_addr;
    struct sockaddr_in clientAddr;
    int addr_len = sizeof(clientAddr);
    if((serverSocket = socket(AF_INET,SOCK_STREAM,0)) < 0) 
    {
        perror( "error: create server socket!!!");
        exit(1);
    }
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family =AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
                    //**** 2-b-bind绑定-设置地址族和端口号等****
    if(bind(serverSocket,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
    {
        perror("error: bind address !!!!");
        exit(1);
    }
                    //**** 3-l-listen 监听来自客户端的请求--客户端主动发连接请求 ****
    if(listen(serverSocket,5)<0)
    {
        perror("error: listen !!!!");
        exit(1);
    }
    gIsRun = 1;
    printf("MesteryServer is running.....\n");
    while(gIsRun)
    {    
        int clientsocket;
                    //**** 4-a-accept 监听到 马上接收数据 ****
        clientsocket = accept(serverSocket,(struct sockaddr *)&clientAddr,(socklen_t*)&addr_len);
        if(clientsocket < 0)
        {
            perror("error: accept client socket !!!");
            continue;
        }
        if(gServerStatus == 0) //本服务器状态如果不良可以直接关闭socket的服务
        {
            close(clientsocket);
        }
        else if(gServerStatus == 1)
        {
            pthread_t threadid;
            int temp;
                    //**** 5-wrc-创建一个线程处理接收到的数据--这是按http协议解析!****
            temp = pthread_create(&threadid, NULL, processthread, (void *)&clientsocket);
            /*if(threadid !=0)
            {                
                 pthread_join(threadid,NULL);
            }*/
        }
    }
    close(serverSocket);
}

程序中可以看见,初始化套接字S后,当绑定B本地服务地址和端口后,便调用listen()函数L进行侦听A,while(gIsRun)表示主服务模块已经启动;然后采用阻塞式等待用户连接的到来,在连接到来的时候,还需要判断gServerStatus的值,即系统是否允许提供服务,如果允许,则创建服务线程。pthread_create(&threadid, NULL, processthread, (void *)&clientsocket);该线程的回调函数为processthread()

void* processthread(void *para)
{
    int clientsocket;
    char buffer[1024];
    int iDataNum =0;
    int recvnum=0;
    clientsocket = *((int *)para);
    printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<BEGIN [%d]>>>>>>>>>>>>>>>>>>>>>>>\n",clientsocket);
    struct HttpRequest httprequest;
    httprequest.content = NULL;
    httprequest.path = NULL;
    httprequest.path = (char *)malloc(1024);
    httprequest.rangeflag = 0;
    httprequest.rangestart = 0;
    while(1)
    {
        iDataNum = recv(clientsocket,buffer+recvnum,sizeof(buffer)-recvnum-1,0);
        if(iDataNum <= 0)
        {
            close(clientsocket);
            pthread_exit(NULL);
            return 0;
        }
        recvnum += iDataNum;
        buffer[recvnum]='\0';
        if(strstr(buffer,"\r\n\r\n")!=NULL || strstr(buffer,"\n\n")!=NULL)
            break;
    }
    printf("request: %s\n",buffer);
                                                                                                                                                                                                                                                                               
    //解析请求信息并处理请求信息
    switch(getrequest(buffer,&httprequest))
    {
    case GET_COMMON:
        processgetcommon(clientsocket,&httprequest);
        break;
    case GET_CGI:
        processgetcgi(clientsocket,&httprequest);
        break;
    case POST:
        processpost(clientsocket,&httprequest);
        break;
    case HEAD:
        processhead(clientsocket,&httprequest);
        break;
    default:
        break;
    }        
    insertlognode(pfilelog,&httprequest);
    if(httprequest.path != NULL)
        free(httprequest.path);
    if(httprequest.content != NULL)
        free(httprequest.content);
    close(clientsocket);
    printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<END [%d]>>>>>>>>>>>>>>>>>>>>>>>\n",clientsocket);
    pthread_exit(NULL);
}


可以看见,在这个线程里面,便开始对请求进行业务分析了。

   ❷协议解析   这个比较简单,

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET(文件传输内容处理), POST 和 HEAD方法。
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

因为HTTP协议的格式是固定的,所以只用对其按照HTTP的格式进行逐步解析就可以了。

   ❸文件传输

   文件传输是归在GET_COMMON类的

//解析请求信息并处理请求信息
switch(getrequest(buffer,&httprequest))
{
case GET_COMMON:
    processgetcommon(clientsocket,&httprequest); //  文件传输
    break;
case GET_CGI:
    processgetcgi(clientsocket,&httprequest);
    break;
case POST:
    processpost(clientsocket,&httprequest);
    break;
case HEAD:
    processhead(clientsocket,&httprequest);
    break;
default:
    break;
}

processgetcommon()函数实现如下:

void processgetcommon(int s,struct HttpRequest *prequest)
{
    //先判断文件是否存在
    FILE *fp = isexistfile(prequest->path);
    printf("%s\n",prequest->path);
    struct stat finfo;
    if(fp == NULL)
    {
        responsecode(s,404,prequest);
    }
    else
    {    
        if(prequest->rangeflag == 0)
        {        
            stat(prequest->path,&finfo);
            prequest->rangetotal = finfo.st_size;
        }
        responsecode(s,200,prequest);
        transferfile(s,fp,prequest->rangeflag,prequest->rangestart,prequest->rangetotal);
        fclose(fp);
    }
}

它先会判断有没有这个文件,如果没有,就生成404响应码,其他

当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。
HTTP状态码的英文为HTTP Status Code。
下面是常见的HTTP状态码:(其他的状态码见:https://www.runoob.com/http/http-status-codes.html

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误

如果有,就返回200响应码,然后首先对prequest->rangeflag进行一个判断,看是否是断点续传,然后便开始传输文件,传输文件函数transferfile()如下

int transferfile(int s,FILE *fp,int type,int rangstart,int totallength)
{
    if(type == 1)
    {
        //为1,则表示当前从指定的位置传送文件
        fseek(fp,rangstart,0);   
    }
    int sendnum = 0;
    int segment = 1024;
    while(!feof(fp)&&sendnum < totallength)
    {
        char buf[segment];
        memset(buf,0,1024);
        int i = 0;
        while(!feof(fp) && i < segment && sendnum+i < totallength)
        {    
            buf[i++] = fgetc(fp);                
        }
        if(sendsegment(s,buf,i) == 0)
            return 0;
        sendnum += i;
    }
    return 1;
}

可以看见,具体的传输文件,是调用sendsegment()函数来实现的;在sendsegment()函数里面,就是用的socket里面的send()函数来实现的

int sendsegment(int s, char *buffer,int length)
{
    if(length <= 0)
        return 0;
    printf("%s\n",buffer);
    int result = send(s,buffer,length,0);
    if(result < 0)
        return 0;
    return 1;
}

2.客户端代码 实现如下

sc wrc

#include <stdio.h>
#include "winsock2.h"

#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)
int main()
{
    SOCKET      sSocket = INVALID_SOCKET;
    SOCKADDR_IN stSvrAddrIn = { 0 }; /* 服务器端地址 */
    char        sndBuf[524] = { 0 };
    char        rcvBuf[2048] = { 0 };
    char* pRcv = rcvBuf;
    int         num = 0;
    int         nRet = SOCKET_ERROR;

    WSADATA     wsaData;

    /* HTTP 消息构造开始,这是程序的关键之处 */
    sprintf_s(sndBuf, 1024, "GET / HTTP/1.1\n");
    strcat_s(sndBuf, 1024, "Host: www.baidu.com\r\n\r\n");
    /* HTTP 消息构造结束 */

    /* socket DLL初始化 */
    WSAStartup(MAKEWORD(2, 0), &wsaData);

    stSvrAddrIn.sin_family = AF_INET;
    stSvrAddrIn.sin_port = htons(80);
    stSvrAddrIn.sin_addr.s_addr = inet_addr("14.215.177.39");

    sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    /* 连接 */
    nRet = connect(sSocket, (SOCKADDR*)&stSvrAddrIn, sizeof(SOCKADDR));
    if (SOCKET_ERROR == nRet)
    {
        printf("connect fail!/n");
        return -1;
    }

    /* 发送HTTP请求消息 */
    send(sSocket, (char*)sndBuf, sizeof(sndBuf), 0);

    /* 接收HTTP响应消息 */
    while (1)
    {
        num = recv(sSocket, pRcv, 548, 0);

        pRcv += num;

        if ((0 == num) || (-1 == num))
        {
            break;
        }
    }

    /* 打印响应消息 */
    printf("%s/n", rcvBuf);

    return 0;
}

找到ip地址,咋vs2019上运行、stSvrAddrIn.sin_addr.s_addr = inet_addr("14.215.177.39");

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 14615
Content-Type: text/html
Date: Thu, 14 Jan 2021 14:40:45 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BAIDUID=49AC41EE41B4E747F9263F40C5AF2C7B:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=49AC41EE41B4E747F9263F40C5AF2C7B; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1610635245; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=49AC41EE41B4E7479BE1D7C055B046A4:FG=1; max-age=31536000; expires=Fri, 14-Jan-22 14:40:45 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Traceid: 1610635245047215053811254122749023909846
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1

<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=Edge">
        <link rel="dns-prefetch" href="//s1.bdstatic.com"/>
        <link rel="dns-prefetch" href="//t1.baidu.com"/>
        <link rel="dns-prefetch" href="//t2.baidu.com"/>
        <link rel="dns-prefetch" href="//t3.baidu.com"/>
        <link rel="dns-prefetch" href="//t10.baidu.com"/>
        <link rel="dns-prefetch" href="//t11.baidu.com"/>
        <link rel="dns-prefetch" href="//t12.baidu.com"/>
        <link rel="dns-prefetch" href="//b1.bdstatic.com"/>
        <title>鐧惧害涓€涓嬶紝浣犲氨鐭ラ亾</title>
        <link href="http://s1.bdstatic.com/r/www/cache/static/home/css/index.css" rel="stylesheet" type="text/css" />
        <!--[if lte IE 8]><style index="index" >#content{height:480px\9}#m{top:260px\9}</style><![endif]-->
        <!--[if IE 8]><style index="index" >#u1 a.mnav,#u1 a.mnav:visited{font-family:simsun}</style><![endif]-->
        <script>var hashMatch = document.location.href.match(/#+(.*wd=[^&].+)/);if (hashMatch && hashMatch[0] && hashMatch[1]) {document.location.replace("http:/R$+location.host+"/s?"+hashM[1]);}var ns_c = function(){};</script>
        <script>function h(obj){obj.style.behavior='urWinSock 2.0/n

 


 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxilang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值