LWIP提供了三种的可以被应用程序直接调用的接口API:
(1) 低水平的,基于内核/回调函数的API(后面称 RAW API) 适用于数据量不大,没有os的MCU
(2) 高水平的,连续的API(后面称LwIP API) 这种方式最常用,需要os支持,适用于传输数据量大的场合
(3) BSD风格的套接字API(后面称BSD socket) 目前还不太稳定
本文介绍的是处于传输层的udp和tcp。两者的区别和各自使用的场合这里就不再赘叙
TCP/IP网络四层模型
1.RAW_UDP
(1).udp简介
端口号表示发送和接收进程, UDP 协议使用端口号为不同的应用保留各自的数据传输通,UDP 和 TCP 协议都是采用端口号对同一时刻内多项应用同时发送和接收数据,而数据接收方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为 UDP 报头使用两个字节存放端口号,所以端口号的有效范围是从 0 到 65535。一般来说,大于 49151 的端口号都代表动态端口。据报的长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为 65535 字节。UDP 协议使用报头中的校验和来保证数据的安全 。
在LWIP中有关处理的函数关系如下:
(2)udp整个通信过程如下图:
(3)其实通讯过程很简单,下面看一下代码
① 接收函数就是遍历整个pbuf链表,将pbuf的数据存储区域payload里面的数据memcpy到数组里

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|=1<<6; //标记接收到数据了 21 pbuf_free(p);//释放内存 22 } 23 else 24 { 25 debug("connect has been break !!!! "); 26 } 27 }
② 发送函数首先为pbuf申请内存,然后将要发送的内容复制到pbuf结构体中,最后发送出去

1 //UDP服务器发送数据 2 void udp_demo_senddata(struct udp_pcb *upcb) 3 { 4 struct pbuf *ptr; 5 ptr=pbuf_alloc(PBUF_TRANSPORT,strlen((char*)udp_demo_sendbuf),PBUF_POOL); //申请内存 6 if(ptr) 7 { 8 pbuf_take(ptr,(char*)udp_demo_sendbuf,strlen((char*)udp_demo_sendbuf)); //将tcp_demo_sendbuf中的数据打包进pbuf结构中 9 udp_send(upcb,ptr); //udp发送数据 10 pbuf_free(ptr);//释放内存 11 } 12 }
③ 关闭连接就是先断开连接,然后删除pcb控制块

1 void udp_demo_connection_close(struct udp_pcb *upcb) 2 { 3 udp_disconnect(upcb); 4 udp_remove(upcb); //断开UDP连接 5 udp_demo_flag &= ~(1<<5); //标记连接断开 6 debug("connect close!!! "); 7 }
④ 测试函数就是按照上图的流程,很简单,不要忘记在测试函数循环中,调用LWIP内核需要定时处理的函数 sys_check_timeouts();

1 //UDP 测试全局状态标记变量 2 //bit7:没有用到 3 //bit6:0,没有收到数据;1,收到数据了. 4 //bit5:0,没有连接上;1,连接上了. 5 //bit4~0:保留 6 u8 udp_demo_flag = 0; 7 struct ip4_addr rmtipaddr; //远端ip地址 8 extern uint8_t REMOTE_IP_ADDRESS[4]; 9 void udp_demo_test(void) 10 { 11 u8 *tbuf; 12 err_t err; 13 u8 res; 14 struct udp_pcb *udppcb; //定义一个TCP服务器控制块 15 struct ip4_addr rmtipaddr; //远端ip地址 16 17 tbuf=malloc(100); //申请内存 18 if(tbuf==NULL)return; 19 20 udppcb=udp_new(); 21 if(udppcb) 22 { 23 IP4_ADDR(&rmtipaddr,REMOTE_IP_ADDRESS[0],REMOTE_IP_ADDRESS[1],REMOTE_IP_ADDRESS[2],REMOTE_IP_ADDRESS[3]); 24 err=udp_connect(udppcb,&rmtipaddr,UDP_DEMO_PORT); //UDP客户端连接到指定IP地址和端口号的服务器 25 26 if(err==ERR_OK) 27 { 28 err=udp_bind(udppcb,IP_ADDR_ANY,UDP_DEMO_PORT);//绑定本地IP地址与端口号 29 if(err==ERR_OK) //绑定完成 30 { 31 udp_recv(udppcb,udp_demo_recv,NULL);//注册接收回调函数 32 debug("STATUS Connected "); 33 udp_demo_flag |= 1<<5; //标记已经连接上 34 debug("Recv Data: "); 35 }else res=1; 36 }else res=1; 37 }else res=1; 38 while(res==0) 39 { 40 if(udp_demo_flag&1<<6)//是否收到数据? 41 { 42 udp_demo_flag&=~(1<<6);//标记数据已经被处理了. 43 udp_demo_senddata(udppcb); 44 debug("%s ",udp_demo_recvbuf); 45 } 46 MX_LWIP_Process(); 47 delay_us(100); 48 } 49 udp_demo_connection_close(udppcb); 50 free(tbuf); 51 }
2.RAW TCP CLIENT
LWIP的各函数之间的关系如下图所示
可以看一下void tcp_client_test(void)这个测试函数,前面部分和udp一样,创建pcb控制块,然后直接调用tcp_connect进行连接,不过需要注册连接成功后的回调函数

1 //TCP Client 测试全局状态标记变量 2 //bit7:0,没有数据要发送;1,有数据要发送 3 //bit6:0,没有收到数据;1,收到数据了. 4 //bit5:0,没有连接上服务器;1,连接上服务器了. 5 //bit4~0:保留 6 u8 tcp_client_flag; 7 8 void tcp_client_test(void) 9 { 10 struct tcp_pcb *tcppcb; //定义一个TCP服务器控制块 11 struct ip4_addr rmtipaddr; //远端ip地址 12 13 u8 *tbuf; 14 u8 res=0; 15 u8 t=0; 16 tbuf = malloc(200); 17 if(tbuf==NULL)return ; 18 tcppcb=tcp_new(); 19 if(tcppcb) 20 { 21 IP4_ADDR(&rmtipaddr,REMOTE_IP_ADDRESS[0],REMOTE_IP_ADDRESS[1],REMOTE_IP_ADDRESS[2],REMOTE_IP_ADDRESS[3]); 22 tcp_connect(tcppcb,&rmtipaddr,TCP_CLIENT_PORT,tcp_client_connected); 23 }else res = 1; 24 delay_ms(200); 25 26 while(!res) 27 { 28 if(tcp_client_flag&1<<6) 29 { 30 debug("recv data: "); 31 debug("%s ",tcp_client_recvbuf); 32 tcp_client_usersent(tcppcb); 33 tcp_client_flag&=~(1<<6); 34 } 35 36 MX_LWIP_Process(); 37 delay_ms(2); 38 t++; 39 if(t==200) 40 { 41 if((tcp_client_flag&1<<5)==0) 42 { 43 tcp_client_connection_close(tcppcb,0);//关闭连接 44 tcppcb=tcp_new(); //创建一个新的pcb 45 if(tcppcb) //创建成功 46 { 47 tcp_connect(tcppcb,&rmtipaddr,TCP_CLIENT_PORT,tcp_client_connected); 48 } 49 } 50 t=0; 51 } 52 } 53 tcp_client_connection_close(tcppcb,0); 54 debug("TCP CLIENT BREAK! "); 55 free(tbuf); 56 57 }
err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
此函数主要是完成对接收、发送、错误、轮询函数的注册

1 err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) 2 { 3 4 5 if(err==ERR_OK) 6 { 7 es=(struct tcp_client_struct*)malloc(sizeof(struct tcp_client_struct)); //申请内存 8 if(es) //内存申请成功 9 { 10 11 es->state=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|=1<<5; //标记连接到服务器了 20 debug("tcp client connected !!! "); 21 err=ERR_OK; 22 }else 23 { 24 debug("tcp client break !!! "); 25 tcp_client_connection_close(tpcb,es);//关闭连接 26 err=ERR_MEM; //返回内存分配错误 27 } 28 }else 29 { 30 tcp_client_connection_close(tpcb,0);//关闭连接 31 } 32 return err; 33 }
err_t tcp_client_recv(void *arg,struct tcp_pcb *tpcb,struct pbuf *p,err_t err)
判断数据接收状态,运行UDP那一套

1 err_t tcp_client_recv(void *arg,struct tcp_pcb *tpcb,struct pbuf *p,err_t err) 2 { 3 u32 data_len = 0; 4 struct pbuf *q; 5 // struct tcp_client_struct *es; 6 err_t ret_err; 7 if(p==NULL)//如果从服务器接收到空的数据帧就关闭连接 8 { 9 es->state=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|=1<<6; //标记接收到数据了 31 tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据 32 pbuf_free(p); //释放内存 33 ret_err=ERR_OK; 34 } 35 }else //接收到数据但是连接已经关闭, 36 { 37 tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据 38 es->p=NULL; 39 pbuf_free(p); //释放内存 40 ret_err=ERR_OK; 41 } 42 return ret_err; 43 }
err_t tcp_client_usersent(struct tcp_pcb *tpcb)
和udp一样

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&=~(1<<7); //清除数据发送标志 15 if(es->p)pbuf_free(es->p); //释放内存 16 ret_err=ERR_OK; 17 }else 18 { 19 tcp_abort(tpcb);//终止连接,删除pcb控制块 20 ret_err=ERR_ABRT; 21 } 22 return ret_err; 23 }
err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
tcp周期性调用的函数,可以用来检测连接状态

//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 "); tcp_client_connection_close(tpcb,es); //关闭TCP连接 } ret_err=ERR_OK; return ret_err; }
err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
检测到远程主机的应答后,发送函数

//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->len<=tcp_sndbuf(tpcb))) { ptr=es->p; 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); //将发送缓冲队列中的数据立即发送出去 } }
void tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct * es)
删除PCB控制块,将回调函数指向空

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&=~(1<<5);//标记连接断开了 13 }
3.RAW TCP SERVER
下面是从别的博客复制过来的一张图片,很清晰的描述了整个TCP server的通讯流程,从最底层到应用层
注意:tcp_client是从connect的时候就会注册那四个函数,而tcp_sever是没有connect的,在accpt的时候注册阻塞函数(阻塞函数中注册那四个函数),等待客户端的连接
除了这两个初始化步骤和阻塞函数与客户端不一样,其他的都差不多

1 #include "raw_tcp_server_test.h" 2 3 4 u8 tcp_server_recvbuf[TCP_SERVER_RX_BUFSIZE]; 5 6 const char *tcp_server_sendbuf="Apollo STM32F7 TCP Server send data "; 7 8 //TCP Server 测试全局状态标记变量 9 //bit7:0,没有数据要发送;1,有数据要发送 10 //bit6:0,没有收到数据;1,收到数据了. 11 //bit5:0,没有客户端连接上;1,有客户端连接上了. 12 //bit4~0:保留 13 u8 tcp_server_flag; 14 15 16 17 void tcp_server_test(void) 18 { 19 err_t err; 20 struct tcp_pcb *tcppcbnew; //定义一个TCP服务器控制块 21 struct tcp_pcb *tcppcbconn; //定义一个TCP服务器控制块 22 23 u8 *tbuf; 24 u8 rSes=0; 25 26 tbuf=malloc(200); //申请内存 27 if(tbuf==NULL)return ; //内存申请失败了,直接退出 28 tcppcbnew=tcp_new(); 29 if(tcppcbnew) //创建成功 30 { 31 err=tcp_bind(tcppcbnew,IP_ADDR_ANY,TCP_SERVER_PORT); 32 if(err==ERR_OK) //绑定完成 33 { 34 tcppcbconn=tcp_listen(tcppcbnew); //设置tcppcb进入监听状态 35 tcp_accept(tcppcbconn,tcp_server_accept); //初始化LWIP的tcp_accept的回调函数 36 }else rSes=1; 37 }else rSes=1; 38 while(rSes==0) 39 { 40 if(tcp_server_flag&1<<6)//是否收到数据? 41 { 42 debug("recv data: %s ",tcp_server_recvbuf); 43 tcp_server_flag&=~(1<<6);//标记数据已经被处理了. 44 tcp_server_usersent(tcppcbnew); 45 } 46 MX_LWIP_Process(); 47 delay_ms(2); 48 } 49 tcp_server_connection_close(tcppcbnew,0);//关闭TCP Server连接 50 tcp_server_connection_close(tcppcbconn,0);//关闭TCP Server连接 51 tcp_server_remove_timewait(); 52 memset(tcppcbnew,0,sizeof(struct tcp_pcb)); 53 memset(tcppcbconn,0,sizeof(struct tcp_pcb)); 54 free(tbuf); 55 } 56 struct tcp_server_struct *Ses; 57 err_t tcp_server_accept(void *arg,struct tcp_pcb *newpcb,err_t err) 58 { 59 err_t ret_err; 60 61 LWIP_UNUSED_ARG(arg); 62 LWIP_UNUSED_ARG(err); 63 tcp_setprio(newpcb,TCP_PRIO_MIN);//设置新创建的pcb优先级 64 65 Ses=(struct tcp_server_struct*)mem_malloc(sizeof(struct tcp_server_struct)); //分配内存 66 if(Ses!=NULL) //内存分配成功 67 { 68 Ses->state=ES_TCPSERVER_ACCEPTED; //接收连接 69 Ses->pcb=newpcb; 70 Ses->p=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 "); 78 tcp_server_flag|=1<<5; //标记有客户端连上了 79 ret_err=ERR_OK; 80 }else ret_err=ERR_MEM; 81 return ret_err; 82 } 83 84 err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) 85 { 86 err_t ret_err; 87 u32 data_len = 0; 88 struct pbuf *q; 89 struct tcp_server_struct *Ses; 90 LWIP_ASSERT("arg != NULL",arg != NULL); 91 Ses=(struct tcp_server_struct *)arg; 92 if(p==NULL) //从客户端接收到空数据 93 { 94 Ses->state=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|=1<<6; //标记接收到数据了 116 tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据 117 pbuf_free(p); //释放内存 118 ret_err=ERR_OK; 119 } 120 }else//服务器关闭了 121 { 122 tcp_recved(tpcb,p->tot_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 ",(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&=~(1<<7); //清除数据发送标志 153 if(Ses->p!=NULL)pbuf_free(Ses->p);//释放内存 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->len<=tcp_sndbuf(tpcb))) 193 { 194 ptr=Ses->p; 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<<5);//标记连接断开了 219 } 220 extern void tcp_pcb_purge(struct tcp_pcb *pcb); //在 tcp.c里面 221 extern struct tcp_pcb *tcp_active_pcbs; //在 tcp.c里面 222 extern struct tcp_pcb *tcp_tw_pcbs; //在 tcp.c里面 223 //强制删除TCP Server主动断开时的time wait 224 void tcp_server_remove_timewait(void) 225 { 226 struct tcp_pcb *pcb,*pcb2; 227 u8 t=0; 228 while(tcp_active_pcbs!=NULL&&t<200) 229 { 230 MX_LWIP_Process(); //继续轮询 231 t++; 232 delay_ms(10); //等待tcp_active_pcbs为空 233 } 234 pcb=tcp_tw_pcbs; 235 while(pcb!=NULL)//如果有等待状态的pcbs 236 { 237 tcp_pcb_purge(pcb); 238 tcp_tw_pcbs=pcb->next; 239 pcb2=pcb; 240 pcb=pcb->next; 241 memp_free(MEMP_TCP_PCB,pcb2); 242 } 243 }
******************************************************************
参考资料:
正点原子《stm32f7 lwip开发手册》
沧海一粟的《lwIP协议栈开发嵌入式网络的三种方法分析》
博客园地址:http://www.cnblogs.com/fozu/p/3613804.html