LWIP提供了三种的可以被应用程序直接调用的接口API:
(1) 低水平的,基于内核/回调函数的API(后面称 RAW API) 适用于数据量不大,没有os的MCU
(2) 高水平的,连续的API(后面称LwIP API) 这种方式最常用,需要os支持,适用于传输数据量大的场合
(3) BSD风格的套接字API(后面称BSD socket) 目前还不太稳定
本文介绍的是处于传输层的udp和tcp。两者的区别和各自使用的场合这里就不再赘叙
TCP/IP网络四层模型
![](https://img2018.cnblogs.com/blog/1239425/201905/1239425-20190524083825142-1319316284.png)
1.RAW_UDP
(1).udp简介
![](https://img2018.cnblogs.com/blog/1239425/201905/1239425-20190524084337917-1740050107.png)
端口号表示发送和接收进程, UDP 协议使用端口号为不同的应用保留各自的数据传输通,UDP 和 TCP 协议都是采用端口号对同一时刻内多项应用同时发送和接收数据,而数据接收方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为 UDP 报头使用两个字节存放端口号,所以端口号的有效范围是从 0 到 65535。一般来说,大于 49151 的端口号都代表动态端口。据报的长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为 65535 字节。UDP 协议使用报头中的校验和来保证数据的安全 。
在LWIP中有关处理的函数关系如下:
![](https://img2018.cnblogs.com/blog/1239425/201905/1239425-20190524084732414-1781629079.png)
(2)udp整个通信过程如下图:
![](https://img2018.cnblogs.com/blog/1239425/201905/1239425-20190524090024385-1737458392.png)
(3)其实通讯过程很简单,下面看一下代码 ① 接收函数就是遍历整个pbuf链表,将pbuf的数据存储区域payload里面的数据memcpy到数组里
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //UDP回调函数
2 void udp_demo_recv(void *arg,struct udp_pcb *upcb,struct pbuf *p, struct ip4_addr *addr,u16_t port)
3 {
4 u32 data_len = 0;
5 struct pbuf *q;
6 if(p!=NULL) //接收到不为空的数据时
7 {
8 memset(udp_demo_recvbuf,0,UDP_DEMO_RX_BUFSIZE); //数据接收缓冲区清零
9 for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表
10 {
11 //判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于
12 //的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
13 if(q->len > (UDP_DEMO_RX_BUFSIZE-data_len)) memcpy(udp_demo_recvbuf+data_len,q->payload,(UDP_DEMO_RX_BUFSIZE-data_len));//拷贝数据
14 else memcpy(udp_demo_recvbuf+data_len,q->payload,q->len);
15 data_len += q->len;
16 if(data_len > UDP_DEMO_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出
17 }
18 upcb->remote_ip=*addr; //记录远程主机的IP地址
19 upcb->remote_port=port; //记录远程主机的端口号
20 udp_demo_flag|=1state=ES_TCPCLIENT_CONNECTED;//状态为连接成功
12 es->pcb=tpcb;
13 es->p=NULL;
14 tcp_arg(tpcb,es); //使用es更新tpcb的callback_arg
15 tcp_recv(tpcb,tcp_client_recv); //初始化LwIP的tcp_recv回调功能
16 tcp_err(tpcb,tcp_client_error); //初始化tcp_err()回调函数
17 tcp_sent(tpcb,tcp_client_sent); //初始化LwIP的tcp_sent回调功能
18 tcp_poll(tpcb,tcp_client_poll,1); //初始化LwIP的tcp_poll回调功能
19 tcp_client_flag|=1state=ES_TCPCLIENT_CLOSING;//需要关闭TCP 连接了
10 es->p=p;
11 ret_err=ERR_OK;
12 }else if(err!= ERR_OK)//当接收到一个非空的数据帧,但是err!=ERR_OK
13 {
14 if(p)pbuf_free(p);//释放接收pbuf
15 ret_err=err;
16 }else if(es->state==ES_TCPCLIENT_CONNECTED) //当处于连接状态时
17 {
18 if(p!=NULL)//当处于连接状态并且接收到的数据不为空时
19 {
20 memset(tcp_client_recvbuf,0,TCP_CLIENT_RX_BUFSIZE); //数据接收缓冲区清零
21 for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表
22 {
23 //判断要拷贝到TCP_CLIENT_RX_BUFSIZE中的数据是否大于TCP_CLIENT_RX_BUFSIZE的剩余空间,如果大于
24 //的话就只拷贝TCP_CLIENT_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
25 if(q->len > (TCP_CLIENT_RX_BUFSIZE-data_len)) memcpy(tcp_client_recvbuf+data_len,q->payload,(TCP_CLIENT_RX_BUFSIZE-data_len));//拷贝数据
26 else memcpy(tcp_client_recvbuf+data_len,q->payload,q->len);
27 data_len += q->len;
28 if(data_len > TCP_CLIENT_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出
29 }
30 tcp_client_flag|=1tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
38 es->p=NULL;
39 pbuf_free(p); //释放内存
40 ret_err=ERR_OK;
41 }
42 return ret_err;
43 }
tcp_client_recv
err_t tcp_client_usersent(struct tcp_pcb *tpcb)
和udp一样
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //LWIP数据发送,用户应用程序调用此函数来发送数据
2 //tpcb:TCP控制块
3 //返回值:0,成功;其他,失败
4 err_t tcp_client_usersent(struct tcp_pcb *tpcb)
5 {
6 err_t ret_err;
7 struct tcp_client_struct *es;
8 es=tpcb->callback_arg;
9 if(es!=NULL) //连接处于空闲可以发送数据
10 {
11 es->p=pbuf_alloc(PBUF_TRANSPORT, strlen((char*)tcp_client_sendbuf),PBUF_POOL); //申请内存
12 pbuf_take(es->p,(char*)tcp_client_sendbuf,strlen((char*)tcp_client_sendbuf)); //将tcp_client_sentbuf[]中的数据拷贝到es->p_tx中
13 tcp_client_senddata(tpcb,es);//将tcp_client_sentbuf[]里面复制给pbuf的数据发送出去
14 tcp_client_flag&=~(1p); //释放内存
16 ret_err=ERR_OK;
17 }else
18 {
19 tcp_abort(tpcb);//终止连接,删除pcb控制块
20 ret_err=ERR_ABRT;
21 }
22 return ret_err;
23 }
tcp_client_usersent
err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
tcp周期性调用的函数,可以用来检测连接状态
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//lwIP tcp_poll的回调函数
err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
{
err_t ret_err;
struct tcp_client_struct *es;
es=(struct tcp_client_struct*)arg;
if(es->state==ES_TCPCLIENT_CLOSING) //连接断开
{
debug("poll close\r\n");
tcp_client_connection_close(tpcb,es); //关闭TCP连接
}
ret_err=ERR_OK;
return ret_err;
}
tcp_client_poll
err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
检测到远程主机的应答后,发送函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//lwIP tcp_sent的回调函数(当从远端主机接收到ACK信号后发送数据)
err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
struct tcp_client_struct *es;
LWIP_UNUSED_ARG(len);
es=(struct tcp_client_struct*)arg;
if(es->p)tcp_client_senddata(tpcb,es);//发送数据
return ERR_OK;
}
//此函数用来发送数据
void tcp_client_senddata(struct tcp_pcb *tpcb, struct tcp_client_struct * es)
{
struct pbuf *ptr;
err_t wr_err=ERR_OK;
while((wr_err==ERR_OK)&&es->p&&(es->p->lenp;
wr_err=tcp_write(tpcb,ptr->payload,ptr->len,1); //将要发送的数据加入到发送缓冲队列中
if(wr_err==ERR_OK)
{
es->p=ptr->next; //指向下一个pbuf
if(es->p)pbuf_ref(es->p); //pbuf的ref加一
pbuf_free(ptr); //释放ptr
}else if(wr_err==ERR_MEM)es->p=ptr;
tcp_output(tpcb); //将发送缓冲队列中的数据立即发送出去
}
}
tcp_client_sent
void tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct * es)
删除PCB控制块,将回调函数指向空
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //关闭与服务器的连接
2 void tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct * es)
3 {
4 //移除回调
5 tcp_abort(tpcb);//终止连接,删除pcb控制块
6 tcp_arg(tpcb,NULL);
7 tcp_recv(tpcb,NULL);
8 tcp_sent(tpcb,NULL);
9 tcp_err(tpcb,NULL);
10 tcp_poll(tpcb,NULL,0);
11 if(es)free(es);
12 tcp_client_flag&=~(1p=NULL;
71
72 tcp_arg(newpcb,Ses);
73 tcp_recv(newpcb,tcp_server_recv); //初始化tcp_recv()的回调函数
74 tcp_err(newpcb,tcp_server_error); //初始化tcp_err()回调函数
75 tcp_poll(newpcb,tcp_server_poll,1); //初始化tcp_poll回调函数
76 tcp_sent(newpcb,tcp_server_sent); //初始化发送回调函数
77 debug("new client connected \r\n");
78 tcp_server_flag|=1state=ES_TCPSERVER_CLOSING;//需要关闭TCP 连接了
95 Ses->p=p;
96 ret_err=ERR_OK;
97 }else if(err!=ERR_OK) //从客户端接收到一个非空数据,但是由于某种原因err!=ERR_OK
98 {
99 if(p)pbuf_free(p); //释放接收pbuf
100 ret_err=err;
101 }else if(Ses->state==ES_TCPSERVER_ACCEPTED) //处于连接状态
102 {
103 if(p!=NULL) //当处于连接状态并且接收到的数据不为空时将其打印出来
104 {
105 memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE); //数据接收缓冲区清零
106 for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表
107 {
108 //判断要拷贝到TCP_SERVER_RX_BUFSIZE中的数据是否大于TCP_SERVER_RX_BUFSIZE的剩余空间,如果大于
109 //的话就只拷贝TCP_SERVER_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
110 if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));//拷贝数据
111 else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
112 data_len += q->len;
113 if(data_len > TCP_SERVER_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出
114 }
115 tcp_server_flag|=1tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
123 Ses->p=NULL;
124 pbuf_free(p); //释放内存
125 ret_err=ERR_OK;
126 }
127 return ret_err;
128
129 }
130 //lwIP tcp_err函数的回调函数
131 void tcp_server_error(void *arg,err_t err)
132 {
133 LWIP_UNUSED_ARG(err);
134 debug("tcp error:%x\r\n",(u32)arg);
135 if(arg!=NULL)mem_free(arg);//释放内存
136 }
137
138
139 //LWIP数据发送,用户应用程序调用此函数来发送数据
140 //tpcb:TCP控制块
141 //返回值:0,成功;其他,失败
142 err_t tcp_server_usersent(struct tcp_pcb *tpcb)
143 {
144 err_t ret_err;
145 struct tcp_server_struct *Ses;
146 Ses=tpcb->callback_arg;
147 if(Ses!=NULL) //连接处于空闲可以发送数据
148 {
149 Ses->p=pbuf_alloc(PBUF_TRANSPORT, strlen((char*)tcp_server_sendbuf),PBUF_POOL); //申请内存
150 pbuf_take(Ses->p,(char*)tcp_server_sendbuf,strlen((char*)tcp_server_sendbuf)); //将tcp_server_sentbuf[]中的数据拷贝到Ses->p_tx中
151 tcp_server_senddata(tpcb,Ses); //将tcp_server_sentbuf[]里面复制给pbuf的数据发送出去
152 tcp_server_flag&=~(1p);//释放内存
154 ret_err=ERR_OK;
155 }else
156 {
157 tcp_abort(tpcb);//终止连接,删除pcb控制块
158 ret_err=ERR_ABRT;
159 }
160 return ret_err;
161 }
162
163
164 //lwIP tcp_poll的回调函数
165 err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb)
166 {
167 err_t ret_err;
168 struct tcp_server_struct *Ses;
169 Ses=(struct tcp_server_struct *)arg;
170 if(Ses->state==ES_TCPSERVER_CLOSING)//需要关闭连接?执行关闭操作
171 {
172 tcp_server_connection_close(tpcb,Ses);//关闭连接
173 }
174 ret_err=ERR_OK;
175 return ret_err;
176 }
177 //lwIP tcp_sent的回调函数(当从远端主机接收到ACK信号后发送数据)
178 err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
179 {
180 struct tcp_server_struct *Ses;
181 LWIP_UNUSED_ARG(len);
182 Ses = (struct tcp_server_struct *) arg;
183 if(Ses->p)tcp_server_senddata(tpcb,Ses);//发送数据
184 return ERR_OK;
185 }
186 //此函数用来发送数据
187 void tcp_server_senddata(struct tcp_pcb *tpcb, struct tcp_server_struct *Ses)
188 {
189 struct pbuf *ptr;
190 u16 plen;
191 err_t wr_err=ERR_OK;
192 while((wr_err==ERR_OK)&&Ses->p&&(Ses->p->lenp;
195 wr_err=tcp_write(tpcb,ptr->payload,ptr->len,1);
196 if(wr_err==ERR_OK)
197 {
198 plen=ptr->len;
199 Ses->p=ptr->next; //指向下一个pbuf
200 if(Ses->p)pbuf_ref(Ses->p); //pbuf的ref加一
201 pbuf_free(ptr);
202 tcp_recved(tpcb,plen); //更新tcp窗口大小
203 }else if(wr_err==ERR_MEM)Ses->p=ptr;
204 tcp_output(tpcb); //将发送缓冲队列中的数据立即发送出去
205 }
206 }
207
208 //关闭tcp连接
209 void tcp_server_connection_close(struct tcp_pcb *tpcb, struct tcp_server_struct *Ses)
210 {
211 tcp_close(tpcb);
212 tcp_arg(tpcb,NULL);
213 tcp_sent(tpcb,NULL);
214 tcp_recv(tpcb,NULL);
215 tcp_err(tpcb,NULL);
216 tcp_poll(tpcb,NULL,0);
217 if(Ses)mem_free(Ses);
218 tcp_server_flag&=~(1 |