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 !!!!
");
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!!!
");
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 
");
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 }
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
",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 }
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 !!!
");
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 }
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
");
         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
";
  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 }
tcp_server_test

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

参考资料:

正点原子《stm32f7 lwip开发手册》

 沧海一粟的《lwIP协议栈开发嵌入式网络的三种方法分析》

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

原文地址:https://www.cnblogs.com/st-home/p/10916850.html