0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于LwIP的HTTP客户端设计

CHANBAEK 来源:木南创智 作者:尹家军 2022-12-14 15:19 次阅读

前面我们实现了TCP服务器和客户端的简单应用,接下来我们实现一个基于TCP协议的应用协议,那就是HTTP超文本传输协议

1 HTTP****协议简介

超文本传输协议(Hyper Text Transfer Protocol),简称HTTP,是一种基于TCP的应用层协议,也是目前为止最为流行的应用层协议之一,可以说HTTP协议是万维网的基石。

HTTP是一种客户端请求、服务器应答式的应用层传输协议,也就是说服务器端是不可能主动向客户端发送数据的。在网络正常的情况下请求和响应都是一一对应的。而这个请求和响应也就是后端开发人员经常看到的Request和Response。

首先,我们来看客户器端的请求,HTTP请求报文由请求行、请求头、空白行以及请求体组成。其报文格式如下:

我们来说一说请求行,它由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。需要理解的是请求方法,HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT几种。先对常用的几种说明如下:

  • GET****方法 ,意思是获取URL指定的资源,这个请求方式是最简单的也是最常用的。使用GET 方法时,可以将请求参数和对应的值附加在 URI 后面,利用一个问号(“?”)将资源的URI和请求参数隔开,参数之间使用与符号(“&”)隔开,因此传递参数长度也受到了限制,而且与隐私相关的信息也直接暴露在URI中。比如/index.jsp?username=holmofy&password=123123
  • HEAD****方法 ,与GET用法相同,但没有响应体,使用场合没有GET多。比如下载前使用HEAD发送请求,通过ContentLength响应字段,来了解网络资源的大小;或者通过LastModified响应字段来判断本地缓存资源是否要更新。
  • POST****方法 ,一般用提交信息或数据,请求服务器进行处理(例如提交表单或者上传文件)。表单使用POST相对GET来说还是比较隐秘的,而且GET的URL有长度限制,而上传大文件就必须要使用POST了。
  • OPTIONS****方法 ,该方法用于请求服务器告知其支持哪些其他的功能和方法。通过OPTIONS 方法,可以询问服务器具体支持哪些方法,或者服务器会使用什么样的方法来处理一些特殊资源。可以说这是一个探测性的方法,客户端通过该方法可以在不访问服务器上实际资源的情况下就知道处理该资源的最优方式。这个选项在跨域HTTP请求的情况出现的比较多,这里有一片关于跨域请求的文章,其中有一张图很好的解释了什么是跨域HTTP请求。

客户端发出HTTP请求,服务端接收后,会向客户端发送响应信息。所以接下来,我们来看看服务器端的响应报文。HTTP响应报文由响应行、响应头、空白行以及响应体组成。其报文格式如下:

在响应报文中,非常重要的就是响应行,其中响应行中最重要的就是HTTP的状态码。HTTP协议中状态码有三位数字组成,第一位数字定义了响应的类别,有以下五种:

  • 1XX :信息提示。表示请求已被服务器接受,但需要继续处理,范围为100~101。
  • 2XX :请求成功。服务器成功处理了请求。范围为200~206。
  • 3XX :客户端重定向。重定向状态码用于告诉客户端浏览器,它们访问的资源已被移动,并告诉客户端新的资源位置。客户端收到重定向会重新对新资源发起请求。范围为300~305。
  • 4XX :客户端信息错误。客户端可能发送了服务器无法处理的东西,比如请求的格式错误,或者请求了一个不存在的资源。范围为400~415。
  • 5XX :服务器出错。客户端发送了有效的请求,但是服务器自身出现错误,比如Web程序运行出错。范围是500~505。

我们开发过程有一些状态码比较常见,我们对其简单说明如下:

2 HTTP****客户端设计

我们已经说过了,HTTP协议是基于TCP运行的,那么佷显然我们要实现一个HTTP客户端其本质上首先是要实现一个TCP客户端。

在实现TCP客户端的基础上,我们要让这个客户端能够实现HTTP协议的基本操作。所以我们需要为客户端构造请求报文。关于请求报文的格式前面已经介绍过了,我们根据这个格式来构造,因为我们只是简单的一个HTTP客户端测试,所以我们采用GET方法。我们构造报文如下:

"GET https://www.cnblogs.com/foxclever/ HTTP/1.1\\r\\n"

"Host:www.cnblogs.com:80\\r\\n\\r\\n";

对于HTTP协议具有专门的端口号,所以我们采用这个制定的端口号来实现。而实现的流程与一般TCP客户端是一样的。

3 HTTP****客户端实现

经过上述的分析以及我们前面实现TCP客户端的经验,实现HTTP客户端已经没有问题。与TCP客户端一般,我们将HTTP客户端分成4个函数来实现。首先依然是实现HTTP客户端的初始化:

1 /* HTTP客户端初始化配置*/
 2  void Http_Client_Initialization(void)
 3 {
 4   struct tcp_pcb *tcp_client_pcb;
 5   ip_addr_t ipaddr;
 6  
 7   /* 将目标服务器的IP写入一个结构体,为pc机本地连接IP地址 */
 8 IP4_ADDR(&ipaddr,httpServerIP[0],httpServerIP[1],httpServerIP[2],httpServerIP[3]);
 9  
10   /* 为tcp客户端分配一个tcp_pcb结构体    */
11   tcp_client_pcb = tcp_new();
12  
13   /* 绑定本地端号和IP地址 */
14   tcp_bind(tcp_client_pcb, IP_ADDR_ANY, TCP_HTTP_CLIENT_PORT);
15  
16   if (tcp_client_pcb != NULL)
17   {
18     /* 与目标服务器进行连接,参数包括了目标端口和目标IP */
19     tcp_connect(tcp_client_pcb, &ipaddr, TCP_HTTP_SERVER_PORT, HTTPClientConnected);
20    
21     tcp_err(tcp_client_pcb, HTTPClientConnectError);
22   }
23 }

我们很容易发现,上述初始化的代码其实就是TCP客户端的初始化代码,除了所使用的端口不一样外,其它都一样。也是在初始化代码中实现了两个函数的注册:一是使用tcp_connect注册连接完成的处理回调函数;二是使用tcp_err注册了连接错误处理回调函数。很明显接下来我们需要实现这两个函数。

连接到服务器成功后的回调函数是tcp_connected_fn类型。在客户端建立一个连接后,内核会调用这个函数。在这个函数中,客户端回想服务器发送最初的操作请求,并且会在这个函数中注册数据接收处理回调函数。

1 /* HTTP客户端连接到服务器回调函数 */
 2 static err_t HTTPClientConnected(void *arg, struct tcp_pcb *pcb, err_t err)
 3 {
 4   char clientString[]="GET https://www.cnblogs.com/foxclever/ HTTP/1.1\\r\\n"
 5 
 6                   "Host:www.cnblogs.com:80\\r\\n\\r\\n";
 7  
 8   /* 配置接收回调函数 */
 9   tcp_recv(pcb, HTTPClientCallback);
10  
11   /* 发送一个建立连接的问候字符串*/
12   tcp_write(pcb,clientString, strlen(clientString),0);
13  
14   return ERR_OK;
15 }

这个代码也是与普通TCP客户端一样,只是为了应用于HTTP协议,我们发送的请求字符串需要按照HTTP的格式来设定。对HTTP客户端连接服务器错误回调函数,它是tcp_err_fn类型,在这个程序中主要完成连接异常结束时的一些处理,可以释放一些必要的资源。在这个函数被内核调用时,连接实际上已经断开,相关控制块也已经被删除。所以在这个函数中我们可以重新初始化连接及其资源。在这里我们就是使用它来重新初始化TCP客户端。

1 /* HTTP客户端连接服务器错误回调函数 */
2 static void HTTPClientConnectError(void *arg, err_t err)
3 {
4   /* 重新启动连接 */
5   Http_Client_Initialization();
6 }

最后我们需要实现的是HTTP客户端接收到数据后的数据处理回调函数。这个函数其实就是我们前面连接成功时,注册过的HTTP客户端数据接收处理函数。这个函数是tcp_recv_fn类型。这是使用RAW API实现HTTP客户端功能最重要的一个函数,因为它决定HTTP客户端的具体功能。

1 /* HTTP客户端接收到数据后的数据处理回调函数 */
 2 static err_t HTTPClientCallback(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)
 3 {
 4   struct pbuf *tcp_send_pbuf;
 5   char echoString[]="GET https://www.cnblogs.com/foxclever/ HTTP/1.1\\r\\n"
 6 
 7                  "Host:www.cnblogs.com:80\\r\\n\\r\\n";
 8  
 9   if (tcp_recv_pbuf != NULL)
10   {
11     /* 更新接收窗口 */
12     tcp_recved(pcb, tcp_recv_pbuf->tot_len);
13  
14     /* 将接收到的服务器内容回显*/
15     tcp_write(pcb,echoString, strlen(echoString), 1);
16     tcp_send_pbuf = tcp_recv_pbuf;
17     tcp_write(pcb, tcp_send_pbuf->payload, tcp_send_pbuf->len, 1);
18  
19     pbuf_free(tcp_recv_pbuf);
20   }
21   else if (err == ERR_OK)
22   {
23     tcp_close(pcb);
24     Http_Client_Initialization();
25  
26     return ERR_OK;
27   }
28  
29   return ERR_OK;
30 }

同样,这段代码也是除了要按HTTP协议构造响应信息外,其他部分与普通TCP客户端类似。

4 、结论

与前一篇实现HTTP服务器是基于TCP服务器实现的一样,这里我们实现HTTP客户端是基于TCP客户端来实现的。在我们前面已经实现TCP客户端的情况下,开发HTTP客户端应用就显得简单了。在这一篇我们基于LwIP实现了一个简单的HTTP客户端应用,我们并对其进行了简单的测试。再历程中我们只是实现了GET方法,但经测试设计是正确的。如果需要设计其他方法的HTTP应用只需在此基础上添加即可。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 服务器
    +关注

    关注

    12

    文章

    8105

    浏览量

    82485
  • HTTP
    +关注

    关注

    0

    文章

    464

    浏览量

    30310
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1270

    浏览量

    78282
  • 客户端
    +关注

    关注

    1

    文章

    282

    浏览量

    16337
  • LwIP
    +关注

    关注

    1

    文章

    82

    浏览量

    26619
收藏 人收藏

    评论

    相关推荐

    stm32 +lwip1.3.1客户端异常导致网络ping不通怎么解决?

    stm32上运行lwip作为服务器,如果客户端异常了,发现在客户端再也ping不通 网络如果异常,交换机挂了,重启后,客户端怎么都ping不通stm32 有没有遇到同样的问题? 不是
    发表于 04-22 07:30

    ucos2中lwip tcp做客户端

    我用的是lwip1.40,我照着官网上l.40的tcp服务器例程写的代码,感觉还行,随时可以链接,断开后,也可以连接,但我自己写的客户端,先链接 在断开,在链接就不能通讯数据了,就是没有重连机制,有没有大神有具有重连机制的客户端
    发表于 06-17 16:35

    是否有HTTP客户端的示例

    V2013-0615是HTTP客户端的示例。它的功能就像通过HTTP API获得或发布到主机。例如,它可以从GooGoLo.com获取数据和发布数据。谢谢。 以上来自于百度翻译 以下为原文 Hi
    发表于 03-01 12:42

    lwip客户端不能传输串口的动态数据

    lwip客户端例程的基础上修改回调函数 声明一个全局sendbuf[50],把串口的数据放在sendbuf[]中,通过lwIP tcp_poll发送,打开网络调试助手和串口调试助手调试,发现
    发表于 06-27 03:55

    请问uIP和Lwip哪个可以设置客户端和服务器的名字?

    uIP和Lwip哪个可以设置客户端和服务器的名字吗?
    发表于 07-08 04:35

    如何解决lwip的netconn客户端发送问题

    我的板子做客户端,用的lwip,现在在与服务器连接成功后,发送有个问题1,在网络调试助手中设置20ms不停发送1字节,板子客户端在接收到服务器的数据后,直接返回数据, 总共发送10000字节,调试
    发表于 07-15 04:36

    如何让探索者LWIP NETCONN TCPsever连线多个客户端

    探索者LWIP NETCONN TCPsever怎么让多个客户端连线,我现在用的例程只能实现一哥客户端连线
    发表于 10-23 02:45

    如何在Lwip做服务应用时实现多个客户端连接?

    请问Lwip做服务应用时,怎么实现多个客户端同时连接它(带操作系统)!!!请大家指点一下!
    发表于 11-11 01:06

    LWIP作服务器连接第一个客户端后,以后的客户端就连接不上

    我用的F407,UCOSII+LWIP作为服务器,现在情况是如果客户端只有一个的话连接是正常的,但是如果第一个客户端连接上,以后的客户端就连接不上了。原子的教程没有这方面的内容,不知道
    发表于 03-06 04:35

    请问LWIP TCPserver支持多个客户端连接吗?

    有些郁闷 来向坛友询问了LWIP TCPserver能做出来 就是使用原子的例程改的但无法支持多个客户端的连接有人写出多个客户端可以连接的server程序吗? 分享下代码 不用把整个工程发出来
    发表于 03-15 23:23

    怎样去设计嵌入式LWIP网络客户端

    嵌入式LWIP网络客户端设计教程本文引用地址: 嵌入式技术的兴起使得传统的基于PC机的互联网技术优势不再,嵌入式网络客户端与服务技术成为热点,而该技术需要移植性高、占用资源小的协议栈
    发表于 08-06 06:46

    stm32f107vc lwip tcp客户端

    stm32f107vc lwip tcp客户端 服务器数据传输第一篇TCP客户端模式简单数据收发 ----控制开发板LED灯概要建立LWIP客户端
    发表于 08-06 09:17

    如何去实现stm32f107vc lwip tcp客户端服务器的数据传输呢

    怎么去建立LWIP客户端模式呢?如何去实现stm32f107vc lwip tcp客户端服务器的数据传输呢?
    发表于 11-04 06:54

    客户端初始化后是否可以从客户端句柄中获取客户端配置呢?

    客户端初始化后是否可以从客户端句柄中获取客户端配置?例如:代码:char name[] = "example";esp_http_client_config_t cfg
    发表于 03-02 06:58

    基于LwIP的TCP客户端设计

    上一篇我们基于LwIP协议栈的RAW API实现了一个TCP服务器的简单应用,接下来一节我们来实现一个TCP客户端的简单应用。
    的头像 发表于 12-14 15:12 1744次阅读
    基于<b class='flag-5'>LwIP</b>的TCP<b class='flag-5'>客户端</b>设计