STM32之LWIP协议栈应用

时间:2023-01-06 01:08:28

1.LWIP介绍

lwip是瑞典计算机科学院网络嵌入式系统小组(SICS)的Adam Dunkels(亚当·邓克尔) 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持 TCP 协议主要功能的基础上减少对RAM的占用。

      LwIP是Light Weight(轻型)IP 协议,有无操作系统的支持都可以运行。LwIP 实现的重点是在保持TCP协议 主要功能的基础上减少对RAM的占用,它只需十几KB的RAM和 40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。

STM32移植LWIP协议栈示例:https://blog.51cto.com/u_15688123/6154995

2.RAW LWIP应用

2.1 TCP协议简介

TCP是一种面向连接的、可靠的、基于IP的传输层协议,面向连接意味着两个使用TCP的应用在彼此交换数据之前必须先建立一个TCP连接。当应用层向TCP层发送用于网间传输的、用 8位字节表示的数据流,TCP则把数据流分割成适当长度的报文段,最大传输段大小( MSS)通常受该计算机连接的网络的数据链路层的最大传送单元( MTU)限制。之后TCP把数据包传给IP层,由它来通过网络将包传送给接收端的TCP层。

TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端的数据包能被按序接收。然后接收端对已成功收到的字节发回一个相应的确认(ACK);如果发送端在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。

在数据正确性与合法性上,TCP用一个校验和函数来检验数据是否有错误,在发送和接收时都要计算校验和。在保证可靠性上,采用超时重传和捎带确认机制。在流量控制上,采用滑动窗口协议,协议中规定,对于窗口内未经确认的分组需要重传。在拥塞控制上,采用TCP拥塞控制算法。

STM32之LWIP协议栈应用

LWIP提供了很多关于TCP的RAW API编程函数,我们可以使用这些函数来完成有关TCP操作,如下表所示:

STM32之LWIP协议栈应用

2.2 TCP相关函数介绍

1.建立TCP连接函数tcp_new

struct tcp_pcb *tcp_new(void)

函数功能:建立一个新的连接标志(pcb)

形  参:

返回值:pcb 正常建立了连接标志,返回建立的

             NULL  新的pcb内存不可用时

2.绑定IP和端口号tcp_bind

err_t tcp_bind (struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)

函数功能:绑定本地 IP 地址和端口号

形  参:pcb   准备绑定的连接,类似于 BSD 标准中的 Sockets

            Ipaddr 绑定的 IP 地址。如果为 IP_ADDR_ANY,则将连接绑定到所有的本地 IP 地址

            port   绑定的本地端口号。注意:千万不要和其它的应用程序产生冲突

返回值:ERR_OK  正确地绑定了指定的连接

             ERR_USE   指定的端口号已经绑定了一个连接,产生了冲突

3.使指定连接进入监听状态tcp_listen

struct tcp_pcb *tcp_listen (struct tcp_pcb *pcb)

函数功能:使指定的连接开始进入监听状态

形  参: pcb  指定将要进入监听状态的连接

返回值: pcb  返回一个新的连接标志 pcb,它作为一个参数传递给将要被分派的函数。这样做的原因是

               处于监听状态的连接一般只需要较小的内存,于是函数 tcp_listen()就会收回原始连接的内

   存,而重新分配一个较小内存块供处于监听状态的连接使用。

NULL 监听状态的连接的内存块不可用时,返回 NULL。如果这样的话,作为参数传递给函

  数tcp_listen()的 pcb 所占用的内存将不能够被分配。

4.等待客户端连接tcp_accept

void tcp_accept(struct tcp_pcb *pcb,err_t (* accept)(void *arg,struct tcp_pcb *newpcb,err_t err))

函数功能:指定处于监听状态的连接接通后将要调用的回调函数

形  参:pcb 指定一个处于监听状态的连接

accept 指定连接接通后将要调用的回调函数

返回值:

2.3 创建服务器示例

#include "lwip_config.h"
#include "lwip/tcp.h"
/*接收成功回调函数*/
u8 buff[1024];
u16 rx_len=0;
err_t tcp_recv_func(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err)
{
	memset(buff,0,sizeof(buff));
	rx_len=0;
	if(p==NULL)
	{
		clinet_stat=0;
		printf("[%d.%d.%d.%d:%d]:客户端断开连接\r\n",(u8)(tpcb->remote_ip.addr),(u8)(tpcb->remote_ip.addr>>8),
                                                (u8)(tpcb->remote_ip.addr>>16),
                                                (u8)(tpcb->remote_ip.addr>>24),
           																			tpcb->remote_port);
	}
	else
	{
		if(p->tot_len==p->len)
		{
			memcpy(buff,p->payload,p->len);
			rx_len=p->len;
			pbuf_free(p);
		}
		else
		{
			struct pbuf *temp=p;
			struct pbuf *q=temp;
			while(temp!=NULL)
			{
				memcpy(buff+rx_len,temp->payload,temp->len);
				q=temp;
				temp=temp->next;
				rx_len+=temp->len;
				pbuf_free(q);
			}
		}
		buff[rx_len]='\0';
		printf("[%d.%d.%d.%d:%d]:%s\r\n",(u8)(tpcb->remote_ip.addr),(u8)(tpcb->remote_ip.addr>>8),
                                     (u8)(tpcb->remote_ip.addr>>16),														
                                     (u8)(tpcb->remote_ip.addr>>24),														
                                     tpcb->remote_port,buff);
	}
	return ERR_OK;
}
/*客户端连接成功回调函数*/
u8 client_addr[4];//IP地址
u16 client_prot=0;
u8 clinet_stat=0;
err_t tcp_client(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	client_addr[0]=newpcb->remote_ip.addr>>0;
	client_addr[1]=newpcb->remote_ip.addr>>8;
	client_addr[2]=newpcb->remote_ip.addr>>16;
	client_addr[3]=newpcb->remote_ip.addr>>24;	
	clinet_stat=1;
	printf("客户端连接成功:%d.%d.%d.%d:%d\r\n",client_addr[0],client_addr[1],client_addr[2],client_addr[3],newpcb->remote_port);
	new_tcp=newpcb;
	tcp_recv(newpcb,tcp_recv_func);
	return ERR_OK;
}
/*TCP服务器创建*/
struct tcp_pcb *new_tcp;//tcp网络信息(套接字)
u8 LWIP_CreateTcpServer(u16 port)
{
	/*1.建立一个新的网卡设备*/
	new_tcp=tcp_new();
	if(new_tcp==NULL)return 1;
	/*2.绑定IP地址和端口号*/
	if(tcp_bind(new_tcp, IP_ADDR_ANY,port)!=ERR_OK)
	{
		return 2;//绑定端口号失败
	}
	/*开始监听*/
	new_tcp=tcp_listen(new_tcp);
	/*等待客户端连接*/
	tcp_accept(new_tcp,tcp_client);
	return 0;
}

2.4 创建TCP客户端

创建TCP客户端步骤:

1.创建新的TCP(tcp_new)--2.设置服务器ip和端口号(IP4_ADDR)--3.连接服务器(tcp_connect)--4.数据收发(tcp_recv、tcp_write);

连接服务器成功回调函数*/
err_t tcp_connect_func(void *arg, struct tcp_pcb *tpcb, err_t err)
{
	client_addr[0]=tpcb->local_ip.addr>>0;
	client_addr[1]=tpcb->local_ip.addr>>8;
	client_addr[2]=tpcb->local_ip.addr>>16;
	client_addr[3]=tpcb->local_ip.addr>>24;	
	new_tcp=tpcb;
	clinet_stat=1;
	printf("%d.%d.%d.%d连接服务器成功\r\n",client_addr[0],client_addr[1],client_addr[2],client_addr[3]);
	tcp_recv(tpcb,tcp_recv_func);
	return 0;
}
/**********TCP客户端创建*****************
**
**形  参:u8 a,u8 b,u8 c,u8 d -- 服务器IP地址
**				u16_t port -- 服务器端口号
**返回值:0 --服务器创建成功,其他值--失败
**
*****************************************/
u8 LWIP_CreateTcpClient(u8 a,u8 b,u8 c,u8 d,u16 port)
{
	ip_addr_t addr;
	/*1.创建新的网卡设备*/
	new_tcp=tcp_new();
	if(new_tcp==NULL)return 1;
	IP4_ADDR(&addr,a,b,c,d);/*将IP地址合成整数*/
	/*连接服务器*/
	tcp_connect(new_tcp,&addr,port,tcp_connect_func);
	return 0;
}

STM32之LWIP协议栈应用

STM32之LWIP协议栈应用