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到数组里
 //UDP回调函数
void udp_demo_recv(void *arg,struct udp_pcb *upcb,struct pbuf *p, struct ip4_addr *addr,u16_t port)
{
u32 data_len = ;
struct pbuf *q;
if(p!=NULL) //接收到不为空的数据时
{
memset(udp_demo_recvbuf,,UDP_DEMO_RX_BUFSIZE); //数据接收缓冲区清零
for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表
{
//判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于
//的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
if(q->len > (UDP_DEMO_RX_BUFSIZE-data_len)) memcpy(udp_demo_recvbuf+data_len,q->payload,(UDP_DEMO_RX_BUFSIZE-data_len));//拷贝数据
else memcpy(udp_demo_recvbuf+data_len,q->payload,q->len);
data_len += q->len;
if(data_len > UDP_DEMO_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出
}
upcb->remote_ip=*addr; //记录远程主机的IP地址
upcb->remote_port=port; //记录远程主机的端口号
udp_demo_flag|=<<; //标记接收到数据了
pbuf_free(p);//释放内存
}
else
{
debug("connect has been break !!!!\r\n");
}
}

udp_demo_recv

    ② 发送函数首先为pbuf申请内存,然后将要发送的内容复制到pbuf结构体中,最后发送出去

 //UDP服务器发送数据
void udp_demo_senddata(struct udp_pcb *upcb)
{
struct pbuf *ptr;
ptr=pbuf_alloc(PBUF_TRANSPORT,strlen((char*)udp_demo_sendbuf),PBUF_POOL); //申请内存
if(ptr)
{
pbuf_take(ptr,(char*)udp_demo_sendbuf,strlen((char*)udp_demo_sendbuf)); //将tcp_demo_sendbuf中的数据打包进pbuf结构中
udp_send(upcb,ptr); //udp发送数据
pbuf_free(ptr);//释放内存
}
}

udp_demo_senddata

③  关闭连接就是先断开连接,然后删除pcb控制块

 void udp_demo_connection_close(struct udp_pcb *upcb)
{
udp_disconnect(upcb);
udp_remove(upcb); //断开UDP连接
udp_demo_flag &= ~(<<); //标记连接断开
debug("connect close!!!\r\n");
}

udp_demo_connection_close

  ④ 测试函数就是按照上图的流程,很简单,不要忘记在测试函数循环中,调用LWIP内核需要定时处理的函数 sys_check_timeouts();

 //UDP 测试全局状态标记变量
//bit7:没有用到
//bit6:0,没有收到数据;1,收到数据了.
//bit5:0,没有连接上;1,连接上了.
//bit4~0:保留
u8 udp_demo_flag = ;
struct ip4_addr rmtipaddr; //远端ip地址
extern uint8_t REMOTE_IP_ADDRESS[];
void udp_demo_test(void)
{
u8 *tbuf;
err_t err;
u8 res;
struct udp_pcb *udppcb; //定义一个TCP服务器控制块
struct ip4_addr rmtipaddr; //远端ip地址 tbuf=malloc(); //申请内存
if(tbuf==NULL)return; udppcb=udp_new();
if(udppcb)
{
IP4_ADDR(&rmtipaddr,REMOTE_IP_ADDRESS[],REMOTE_IP_ADDRESS[],REMOTE_IP_ADDRESS[],REMOTE_IP_ADDRESS[]);
err=udp_connect(udppcb,&rmtipaddr,UDP_DEMO_PORT); //UDP客户端连接到指定IP地址和端口号的服务器 if(err==ERR_OK)
{
err=udp_bind(udppcb,IP_ADDR_ANY,UDP_DEMO_PORT);//绑定本地IP地址与端口号
if(err==ERR_OK) //绑定完成
{
udp_recv(udppcb,udp_demo_recv,NULL);//注册接收回调函数
debug("STATUS Connected \r\n");
udp_demo_flag |= <<; //标记已经连接上
debug("Recv Data: \r\n");
}else res=;
}else res=;
}else res=;
while(res==)
{
if(udp_demo_flag&<<)//是否收到数据?
{
udp_demo_flag&=~(<<);//标记数据已经被处理了.
udp_demo_senddata(udppcb);
debug("%s \r\n",udp_demo_recvbuf);
}
MX_LWIP_Process();
delay_us();
}
udp_demo_connection_close(udppcb);
free(tbuf);
}

udp_demo_test

2.RAW TCP CLIENT

LWIP的各函数之间的关系如下图所示

可以看一下void tcp_client_test(void)这个测试函数,前面部分和udp一样,创建pcb控制块,然后直接调用tcp_connect进行连接,不过需要注册连接成功后的回调函数

 //TCP Client 测试全局状态标记变量
//bit7:0,没有数据要发送;1,有数据要发送
//bit6:0,没有收到数据;1,收到数据了.
//bit5:0,没有连接上服务器;1,连接上服务器了.
//bit4~0:保留
u8 tcp_client_flag; void tcp_client_test(void)
{
struct tcp_pcb *tcppcb; //定义一个TCP服务器控制块
struct ip4_addr rmtipaddr; //远端ip地址 u8 *tbuf;
u8 res=;
u8 t=;
tbuf = malloc();
if(tbuf==NULL)return ;
tcppcb=tcp_new();
if(tcppcb)
{
IP4_ADDR(&rmtipaddr,REMOTE_IP_ADDRESS[],REMOTE_IP_ADDRESS[],REMOTE_IP_ADDRESS[],REMOTE_IP_ADDRESS[]);
tcp_connect(tcppcb,&rmtipaddr,TCP_CLIENT_PORT,tcp_client_connected);
}else res = ;
delay_ms(); while(!res)
{
if(tcp_client_flag&<<)
{
debug("recv data: ");
debug("%s\r\n",tcp_client_recvbuf);
tcp_client_usersent(tcppcb);
tcp_client_flag&=~(<<);
} MX_LWIP_Process();
delay_ms();
t++;
if(t==)
{
if((tcp_client_flag&<<)==)
{
tcp_client_connection_close(tcppcb,);//关闭连接
tcppcb=tcp_new(); //创建一个新的pcb
if(tcppcb) //创建成功
{
tcp_connect(tcppcb,&rmtipaddr,TCP_CLIENT_PORT,tcp_client_connected);
}
}
t=;
}
}
tcp_client_connection_close(tcppcb,);
debug("TCP CLIENT BREAK!\r\n");
free(tbuf); }

tcp_client_test

err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)

此函数主要是完成对接收、发送、错误、轮询函数的注册

 err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{ if(err==ERR_OK)
{
es=(struct tcp_client_struct*)malloc(sizeof(struct tcp_client_struct)); //申请内存
if(es) //内存申请成功
{ es->state=ES_TCPCLIENT_CONNECTED;//状态为连接成功
es->pcb=tpcb;
es->p=NULL;
tcp_arg(tpcb,es); //使用es更新tpcb的callback_arg
tcp_recv(tpcb,tcp_client_recv); //初始化LwIP的tcp_recv回调功能
tcp_err(tpcb,tcp_client_error); //初始化tcp_err()回调函数
tcp_sent(tpcb,tcp_client_sent); //初始化LwIP的tcp_sent回调功能
tcp_poll(tpcb,tcp_client_poll,); //初始化LwIP的tcp_poll回调功能
tcp_client_flag|=<<; //标记连接到服务器了
debug("tcp client connected !!!\r\n");
err=ERR_OK;
}else
{
debug("tcp client break !!!\r\n");
tcp_client_connection_close(tpcb,es);//关闭连接
err=ERR_MEM; //返回内存分配错误
}
}else
{
tcp_client_connection_close(tpcb,);//关闭连接
}
return err;
}

tcp_client_connected

err_t tcp_client_recv(void *arg,struct tcp_pcb *tpcb,struct pbuf *p,err_t err)

判断数据接收状态,运行UDP那一套

 err_t tcp_client_recv(void *arg,struct tcp_pcb *tpcb,struct pbuf *p,err_t err)
{
u32 data_len = ;
struct pbuf *q;
// struct tcp_client_struct *es;
err_t ret_err;
if(p==NULL)//如果从服务器接收到空的数据帧就关闭连接
{
es->state=ES_TCPCLIENT_CLOSING;//需要关闭TCP 连接了
es->p=p;
ret_err=ERR_OK;
}else if(err!= ERR_OK)//当接收到一个非空的数据帧,但是err!=ERR_OK
{
if(p)pbuf_free(p);//释放接收pbuf
ret_err=err;
}else if(es->state==ES_TCPCLIENT_CONNECTED) //当处于连接状态时
{
if(p!=NULL)//当处于连接状态并且接收到的数据不为空时
{
memset(tcp_client_recvbuf,,TCP_CLIENT_RX_BUFSIZE); //数据接收缓冲区清零
for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表
{
//判断要拷贝到TCP_CLIENT_RX_BUFSIZE中的数据是否大于TCP_CLIENT_RX_BUFSIZE的剩余空间,如果大于
//的话就只拷贝TCP_CLIENT_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
if(q->len > (TCP_CLIENT_RX_BUFSIZE-data_len)) memcpy(tcp_client_recvbuf+data_len,q->payload,(TCP_CLIENT_RX_BUFSIZE-data_len));//拷贝数据
else memcpy(tcp_client_recvbuf+data_len,q->payload,q->len);
data_len += q->len;
if(data_len > TCP_CLIENT_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出
}
tcp_client_flag|=<<; //标记接收到数据了
tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
pbuf_free(p); //释放内存
ret_err=ERR_OK;
}
}else //接收到数据但是连接已经关闭,
{
tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
es->p=NULL;
pbuf_free(p); //释放内存
ret_err=ERR_OK;
}
return ret_err;
}

tcp_client_recv

err_t tcp_client_usersent(struct tcp_pcb *tpcb)

和udp一样

 //LWIP数据发送,用户应用程序调用此函数来发送数据
//tpcb:TCP控制块
//返回值:0,成功;其他,失败
err_t tcp_client_usersent(struct tcp_pcb *tpcb)
{
err_t ret_err;
struct tcp_client_struct *es;
es=tpcb->callback_arg;
if(es!=NULL) //连接处于空闲可以发送数据
{
es->p=pbuf_alloc(PBUF_TRANSPORT, strlen((char*)tcp_client_sendbuf),PBUF_POOL); //申请内存
pbuf_take(es->p,(char*)tcp_client_sendbuf,strlen((char*)tcp_client_sendbuf)); //将tcp_client_sentbuf[]中的数据拷贝到es->p_tx中
tcp_client_senddata(tpcb,es);//将tcp_client_sentbuf[]里面复制给pbuf的数据发送出去
tcp_client_flag&=~(<<); //清除数据发送标志
if(es->p)pbuf_free(es->p); //释放内存
ret_err=ERR_OK;
}else
{
tcp_abort(tpcb);//终止连接,删除pcb控制块
ret_err=ERR_ABRT;
}
return ret_err;
}

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,); //将要发送的数据加入到发送缓冲队列中
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控制块,将回调函数指向空

 //关闭与服务器的连接
void tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct * es)
{
//移除回调
tcp_abort(tpcb);//终止连接,删除pcb控制块
tcp_arg(tpcb,NULL);
tcp_recv(tpcb,NULL);
tcp_sent(tpcb,NULL);
tcp_err(tpcb,NULL);
tcp_poll(tpcb,NULL,);
if(es)free(es);
tcp_client_flag&=~(<<);//标记连接断开了
}

tcp_client_connection_close

3.RAW TCP SERVER

下面是从别的博客复制过来的一张图片,很清晰的描述了整个TCP server的通讯流程,从最底层到应用层

注意:tcp_client是从connect的时候就会注册那四个函数,而tcp_sever是没有connect的,在accpt的时候注册阻塞函数(阻塞函数中注册那四个函数),等待客户端的连接

除了这两个初始化步骤和阻塞函数与客户端不一样,其他的都差不多

 #include "raw_tcp_server_test.h"

 u8 tcp_server_recvbuf[TCP_SERVER_RX_BUFSIZE];    

 const char *tcp_server_sendbuf="Apollo STM32F7 TCP Server send data\r\n";

 //TCP Server 测试全局状态标记变量
//bit7:0,没有数据要发送;1,有数据要发送
//bit6:0,没有收到数据;1,收到数据了.
//bit5:0,没有客户端连接上;1,有客户端连接上了.
//bit4~0:保留
u8 tcp_server_flag; void tcp_server_test(void)
{
err_t err;
struct tcp_pcb *tcppcbnew; //定义一个TCP服务器控制块
struct tcp_pcb *tcppcbconn; //定义一个TCP服务器控制块 u8 *tbuf;
u8 rSes=; tbuf=malloc(); //申请内存
if(tbuf==NULL)return ; //内存申请失败了,直接退出
tcppcbnew=tcp_new();
if(tcppcbnew) //创建成功
{
err=tcp_bind(tcppcbnew,IP_ADDR_ANY,TCP_SERVER_PORT);
if(err==ERR_OK) //绑定完成
{
tcppcbconn=tcp_listen(tcppcbnew); //设置tcppcb进入监听状态
tcp_accept(tcppcbconn,tcp_server_accept); //初始化LWIP的tcp_accept的回调函数
}else rSes=;
}else rSes=;
while(rSes==)
{
if(tcp_server_flag&<<)//是否收到数据?
{
debug("recv data: %s\r\n",tcp_server_recvbuf);
tcp_server_flag&=~(<<);//标记数据已经被处理了.
tcp_server_usersent(tcppcbnew);
}
MX_LWIP_Process();
delay_ms();
}
tcp_server_connection_close(tcppcbnew,);//关闭TCP Server连接
tcp_server_connection_close(tcppcbconn,);//关闭TCP Server连接
tcp_server_remove_timewait();
memset(tcppcbnew,,sizeof(struct tcp_pcb));
memset(tcppcbconn,,sizeof(struct tcp_pcb));
free(tbuf);
}
struct tcp_server_struct *Ses;
err_t tcp_server_accept(void *arg,struct tcp_pcb *newpcb,err_t err)
{
err_t ret_err; LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
tcp_setprio(newpcb,TCP_PRIO_MIN);//设置新创建的pcb优先级 Ses=(struct tcp_server_struct*)mem_malloc(sizeof(struct tcp_server_struct)); //分配内存
if(Ses!=NULL) //内存分配成功
{
Ses->state=ES_TCPSERVER_ACCEPTED; //接收连接
Ses->pcb=newpcb;
Ses->p=NULL; tcp_arg(newpcb,Ses);
tcp_recv(newpcb,tcp_server_recv); //初始化tcp_recv()的回调函数
tcp_err(newpcb,tcp_server_error); //初始化tcp_err()回调函数
tcp_poll(newpcb,tcp_server_poll,); //初始化tcp_poll回调函数
tcp_sent(newpcb,tcp_server_sent); //初始化发送回调函数
debug("new client connected \r\n");
tcp_server_flag|=<<; //标记有客户端连上了
ret_err=ERR_OK;
}else ret_err=ERR_MEM;
return ret_err;
} err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
err_t ret_err;
u32 data_len = ;
struct pbuf *q;
struct tcp_server_struct *Ses;
LWIP_ASSERT("arg != NULL",arg != NULL);
Ses=(struct tcp_server_struct *)arg;
if(p==NULL) //从客户端接收到空数据
{
Ses->state=ES_TCPSERVER_CLOSING;//需要关闭TCP 连接了
Ses->p=p;
ret_err=ERR_OK;
}else if(err!=ERR_OK) //从客户端接收到一个非空数据,但是由于某种原因err!=ERR_OK
{
if(p)pbuf_free(p); //释放接收pbuf
ret_err=err;
}else if(Ses->state==ES_TCPSERVER_ACCEPTED) //处于连接状态
{
if(p!=NULL) //当处于连接状态并且接收到的数据不为空时将其打印出来
{
memset(tcp_server_recvbuf,,TCP_SERVER_RX_BUFSIZE); //数据接收缓冲区清零
for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表
{
//判断要拷贝到TCP_SERVER_RX_BUFSIZE中的数据是否大于TCP_SERVER_RX_BUFSIZE的剩余空间,如果大于
//的话就只拷贝TCP_SERVER_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));//拷贝数据
else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
data_len += q->len;
if(data_len > TCP_SERVER_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出
}
tcp_server_flag|=<<; //标记接收到数据了
tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
pbuf_free(p); //释放内存
ret_err=ERR_OK;
}
}else//服务器关闭了
{
tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
Ses->p=NULL;
pbuf_free(p); //释放内存
ret_err=ERR_OK;
}
return ret_err; }
//lwIP tcp_err函数的回调函数
void tcp_server_error(void *arg,err_t err)
{
LWIP_UNUSED_ARG(err);
debug("tcp error:%x\r\n",(u32)arg);
if(arg!=NULL)mem_free(arg);//释放内存
} //LWIP数据发送,用户应用程序调用此函数来发送数据
//tpcb:TCP控制块
//返回值:0,成功;其他,失败
err_t tcp_server_usersent(struct tcp_pcb *tpcb)
{
err_t ret_err;
struct tcp_server_struct *Ses;
Ses=tpcb->callback_arg;
if(Ses!=NULL) //连接处于空闲可以发送数据
{
Ses->p=pbuf_alloc(PBUF_TRANSPORT, strlen((char*)tcp_server_sendbuf),PBUF_POOL); //申请内存
pbuf_take(Ses->p,(char*)tcp_server_sendbuf,strlen((char*)tcp_server_sendbuf)); //将tcp_server_sentbuf[]中的数据拷贝到Ses->p_tx中
tcp_server_senddata(tpcb,Ses); //将tcp_server_sentbuf[]里面复制给pbuf的数据发送出去
tcp_server_flag&=~(<<); //清除数据发送标志
if(Ses->p!=NULL)pbuf_free(Ses->p);//释放内存
ret_err=ERR_OK;
}else
{
tcp_abort(tpcb);//终止连接,删除pcb控制块
ret_err=ERR_ABRT;
}
return ret_err;
} //lwIP tcp_poll的回调函数
err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb)
{
err_t ret_err;
struct tcp_server_struct *Ses;
Ses=(struct tcp_server_struct *)arg;
if(Ses->state==ES_TCPSERVER_CLOSING)//需要关闭连接?执行关闭操作
{
tcp_server_connection_close(tpcb,Ses);//关闭连接
}
ret_err=ERR_OK;
return ret_err;
}
//lwIP tcp_sent的回调函数(当从远端主机接收到ACK信号后发送数据)
err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
struct tcp_server_struct *Ses;
LWIP_UNUSED_ARG(len);
Ses = (struct tcp_server_struct *) arg;
if(Ses->p)tcp_server_senddata(tpcb,Ses);//发送数据
return ERR_OK;
}
//此函数用来发送数据
void tcp_server_senddata(struct tcp_pcb *tpcb, struct tcp_server_struct *Ses)
{
struct pbuf *ptr;
u16 plen;
err_t wr_err=ERR_OK;
while((wr_err==ERR_OK)&&Ses->p&&(Ses->p->len<=tcp_sndbuf(tpcb)))
{
ptr=Ses->p;
wr_err=tcp_write(tpcb,ptr->payload,ptr->len,);
if(wr_err==ERR_OK)
{
plen=ptr->len;
Ses->p=ptr->next; //指向下一个pbuf
if(Ses->p)pbuf_ref(Ses->p); //pbuf的ref加一
pbuf_free(ptr);
tcp_recved(tpcb,plen); //更新tcp窗口大小
}else if(wr_err==ERR_MEM)Ses->p=ptr;
tcp_output(tpcb); //将发送缓冲队列中的数据立即发送出去
}
} //关闭tcp连接
void tcp_server_connection_close(struct tcp_pcb *tpcb, struct tcp_server_struct *Ses)
{
tcp_close(tpcb);
tcp_arg(tpcb,NULL);
tcp_sent(tpcb,NULL);
tcp_recv(tpcb,NULL);
tcp_err(tpcb,NULL);
tcp_poll(tpcb,NULL,);
if(Ses)mem_free(Ses);
tcp_server_flag&=~(<<);//标记连接断开了
}
extern void tcp_pcb_purge(struct tcp_pcb *pcb); //在 tcp.c里面
extern struct tcp_pcb *tcp_active_pcbs; //在 tcp.c里面
extern struct tcp_pcb *tcp_tw_pcbs; //在 tcp.c里面
//强制删除TCP Server主动断开时的time wait
void tcp_server_remove_timewait(void)
{
struct tcp_pcb *pcb,*pcb2;
u8 t=;
while(tcp_active_pcbs!=NULL&&t<)
{
MX_LWIP_Process(); //继续轮询
t++;
delay_ms(); //等待tcp_active_pcbs为空
}
pcb=tcp_tw_pcbs;
while(pcb!=NULL)//如果有等待状态的pcbs
{
tcp_pcb_purge(pcb);
tcp_tw_pcbs=pcb->next;
pcb2=pcb;
pcb=pcb->next;
memp_free(MEMP_TCP_PCB,pcb2);
}
}

tcp_server_test

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

参考资料:

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

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

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

最新文章

  1. Docker - 入门
  2. Cesium应用篇:3控件(6) FullScreen/ VR / Home
  3. php使用PDO连接mysql数据库
  4. 几种I/O模型功能和性能对比
  5. 个人对AutoResetEvent和ManualResetEvent的理解(转载)
  6. 命令别名alias设置
  7. fatal error C1853: &#39;*.pch&#39; is not a precompiled header file created with this compile
  8. android sdk启动报错error: could not install *smartsocket* listener: cannot bind to 127.0.0.1:5037:
  9. 基于 SquashFS 构建 Linux 可读写文件系统
  10. ReaderWriterLock类(转)
  11. 鼠标移动到表格的TD上的时候显示成一个手型的样子怎么做?
  12. META http-equiv 大全
  13. c++ 03
  14. GCD hdu1695容斥原理
  15. mysql之子查询作业
  16. 前端笔记之NodeJS(四)MongoDB数据库&amp;Mongoose&amp;自制接口&amp;MVC架构思想|实战
  17. Java读取Excel的另一种方法
  18. Navicat for Mysql连接mysql数据库时出现 2003-Can&#39;t connect to MySql server on &#39;localhost&#39;(10061)
  19. 《linux就该这么学》找到一本不错的Linux电子书,《Linux就该这么学》。
  20. 【BZOJ4870】组合数问题(动态规划,矩阵快速幂)

热门文章

  1. 今天遇到 Request failed: method not allowed (405)。 错误,特此在网上翻了翻
  2. 装系统 ------ 使用微PE 做系统盘
  3. Java第三次作业第四题
  4. MOOC web前端开发笔记(一)
  5. springboot jsp,过滤器,拦截器
  6. 洗牌Shuffle&#39;m Up POJ-3087 模拟
  7. logcat粗略了解(一)
  8. 23种设计模式之装饰器模式(Decorator Pattern)
  9. 数据库系统概论——从E-R模型到关系模型
  10. GoLang 获取两个时间相差多少小时