本帖最后由 3guoyangyang7 于 2016-11-5 16:31 编辑
Lwip小狂是好几年前接触,突然玩到ESP32又接触到Lwip感觉特别亲切,哈哈,我当时的毕业设计也是关于Lwip的,哈哈。小狂在前边的帖子中已经说了TCP客户端这一块跟ESP32已经没有什么卵关系了,玩到网络层之后基本上控制权都交给了Lwip了,所以一旦玩到Lwip就不要说什么没有API怎么怎么样了,直接参考Lwip的资料一票票,推荐两本书,都是老衲五木的书,一本是《嵌入式网络那些事:LwIP协议深度剖析与实战演练》另一本是《嵌入式网络那些事—STM32物联实战》讲得都不错,小狂也在研读,要学的东西太多,只能一步步慢慢消化。小狂基于TCP客户端是从原子的探索者移植过来的,下边会慢慢分析,听小狂一一道来,哈哈。
一、概念分析
玩过TCP的童鞋可以跳过这一段,小狂不讲解TCP里边协议的内容,那些太高大上,也不是一句两句话就能解释清楚的,就是说明一下什么叫TCP客户端。
简单介绍一下TCP,TCP是一种面向连接的、可靠的、基于IP的传输层协议,面向连接义务这两个使用TCP的应用在彼此交换数据之前必须先建立一个TCP连接。当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,TCP则吧数据流分割成适当长度的报文段,最大传输段大小(MSS)通常受该计算机连接的网络的数据链路层的最大传输单元(MTU)限制。之后TCP把数据包传给IP层,由它来通过网络将包传送给接收端的TCP层。(这一段是我抄的,那么专业的话,小狂写不出来,哈哈)
下面就说说TCP客户端,一般有客户端就有服务端,对,没错,举个不恰当的例子,你可以吧服务端当做一个店铺,这个店铺有地址,和名字,我们可以把地址理解为客户端的ip地址,名字理解为开放的窗口,就是端口号。客户端从字面意思上来看就是就是客户吗,服务端一直在那,客户有需求就区联系服务端就这样,不知道这样说会不会把大家说迷糊,说白了服务端就是一直等待客户端的到来,连接发送消息,然后断开连接就这样。
一般客户端的使用步骤如下。
1、首先创建一个TCP socket 就是传说中的套接字
2、连接到TCP服务端
3、发送或者接收数据
4、关闭连接
二、代码分析
在这里没有用到Lwip基本的socket API而是用了更为简单的协议栈接口,就是NETCONN编程接口,就把用到的几个函数一一讲解,主要参考的就是《嵌入式网络那些事:LwIP协议深度剖析与实战演练》。有想更深入了解的小伙伴可以去耍一耍。
1、netconn_new
这个函数的功能就是申请一个新的netbuf空间,这里不会分配任何数据空间,不指向任何的pbuf,真正的数据存储区域需要通过调用netbuf_alloc来分配。
tcp_clientconn = netconn_new(NETCONN_TCP);
调用netconn_new函数申请一个TCP连接结构。
2、netconn_connect
netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port),是其函数原型,功能是连接服务器,他将连接结构与目的IP地址的addr和目的的端口号进行绑定,当作为tcp客户端程序时,调用该函数会导致连接握手过程的产生。
err = netconn_connect(tcp_clientconn,&server_ipaddr,server_port);
tcp_clientconn,是上一步申请的netbuf的连接结构,server_ipaddr为服务器的IP地址,IP4_ADDR(&(server_ipaddr.u_addr.ip4),172, 26 , 149, 1);这句话是给server_ipaddr赋值服务器的地址。server_port是服务器开放的端口号。
3、netconn_delete
这个函数的功能是删除一个连接结构netconn,如果函数调用时双方仍然处于连接状态,相应连接将被关闭;对于TCP连接,函数执行主动关闭,内核完成剩余的断开握手过程,因此对于TCP连接来说,执行这个函数后,内核中的TCP控制块并没有立即被删除,内核将在连接完全断开或者超时后删除控制块。
4、netconn_getaddr
这个函数的目的是获得一个连接结构netconn中的源IP地址和源端口号,或者目的IP地址和目的端口号,IP地址信息存在addr中,端口信息保存在port中,参数local指明是获得源地址信息还是目的地址信息,当为1时表示本地,即源地址信息,也即连接的本地IP地址和本地端口号。
err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
5、netconn_write
该函数用于稳定的TCP连接上发送数据,参数dataptr和size分别指出了待发送数据的起始地址和长度,该函数并不要求用户将数据封装在netbuf中,对于数据长度也没有限制,内核将直接处理这些数据,将他们封装在pbuf中,并挂在TCP的发送队列中。
netconn_write(conn, dataptr, size, apiflags)
6、netconn_recv
该函数是从连接的recvmbox邮箱中接收数据包,可用于TCP连接,也可用于UDP连接,函数会一直阻塞,直到从邮箱中获得数据消息,连接数据都封装在netbuf中,如果从邮箱中接收到一条空小小,表示地方已经关闭当前的连接,应用程序也应该关闭这个无效的连接。
netconn_recv(struct netconn *conn, struct netbuf **new_buf)
7、netconn_close
该函数的功能是关闭一个TCP连接,该函数调用会引起一个FIN握手包的发送,成功发送后,函数便返回,而后剩余的断开握手操作由内核自动完成,用户程序不用关系。主义干函数只是断开一个连接,但不会删除连接机构netconn,用户应调用函数Netconn_delete删除连接结构占用的内存空间,否则会造成内存泄漏。
netconn_close(struct netconn *conn)
8、总体代码实现
代码就不一一解释了,上边的内容已经把能解释的东西全部解释完了,就说实现了什么功能首先建立了一个tcp的连接体,然后接入客户端,判断是否接入成功,如果成功的话就获取设备的IP地址,否则的话就删除连接体,没1S发送一次数据,发送完成后判断是否服务端发来数据,如果有就清空接收数组,遍历整个pbuf链表,判断拷贝的数据是否大于数组剩余的空间,如果大于就只拷贝剩余空间的数据,如果不大于就拷贝所有的数据。如果服务端关闭连接就关闭连接,删除连接体。
三、结果
设置网络调试助手为TCP服务器,输入服务器的IP地址,这个一般是你电脑的地址
点击连接,就会收到ESP32发送的数据
然后我们发送数据,看串口是否打印出来
四、总结
至此,关于ESP32的客户端都搞定了,写帖子比较累,更何况小狂写的全是原创,看到的小伙伴就留个脚步吧,给小狂支持,谢谢大家,下一篇TCP 服务器。再次感谢,求分,求支持,求打赏,哈哈。
ESP32其他篇请移步