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 !!!!\r\n"); 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!!!\r\n"); 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 \r\n"); 33 udp_demo_flag |= 1<<5; //標記已經連接上 34 debug("Recv Data: \r\n"); 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 \r\n",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\r\n",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!\r\n"); 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 !!!\r\n"); 21 err=ERR_OK; 22 }else 23 { 24 debug("tcp client break !!!\r\n"); 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\r\n"); 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\r\n"; 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\r\n",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 \r\n"); 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\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&=~(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