RAW網絡編程


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 }
udp_demo_recv

    ② 發送函數首先為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 }
udp_demo_senddata

         ③  關閉連接就是先斷開連接,然后刪除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 }
udp_demo_connection_close

  ④ 測試函數就是按照上圖的流程,很簡單,不要忘記在測試函數循環中,調用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 }
udp_demo_test

 

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 }
tcp_client_test

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 }
tcp_client_connected

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 }
tcp_client_recv

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 }
tcp_client_usersent

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;
} 
tcp_client_poll

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);                //將發送緩沖隊列中的數據立即發送出去
    }     
} 
tcp_client_sent

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 }
tcp_client_connection_close

 

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 }
tcp_server_test

 

******************************************************************

參考資料:

正點原子《stm32f7 lwip開發手冊》

 滄海一粟的《lwIP協議棧開發嵌入式網絡的三種方法分析》

博客園地址:http://www.cnblogs.com/fozu/p/3613804.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM