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

RAW网络编程的更多相关文章

  1. 猫哥网络编程系列:详解 BAT 面试题

    从产品上线前的接口开发和调试,到上线后的 bug 定位.性能优化,网络编程知识贯穿着一个互联网产品的整个生命周期.不论你是前后端的开发岗位,还是 SQA.运维等其他技术岗位,掌握网络编程知识均是岗位的 ...

  2. python学习笔记11 ----网络编程

    网络编程 网络编程需要知道的概念 网络体系结构就是使用这些用不同媒介连接起来的不同设备和网络系统在不同的应用环境下实现互操作性,并满足各种业务需求的一种粘合剂.网络体系结构解决互质性问题彩是分层方法. ...

  3. C#网络编程基础知识

    C#网络编程基础知识一 1.IPAddress类 用于表示一个IP地址.IPAddress默认构造函数 public IPAddress(long address);一般不用 其中Parse()方法最 ...

  4. 用C++实现网络编程---抓取网络数据包的实现方法

    一般都熟悉sniffer这个工具,它可以捕捉流经本地网卡的所有数据包.抓取网络数据包进行分析有很多用处,如分析网络是否有网络病毒等异常数据,通信协议的分析(数据链路层协议.IP.UDP.TCP.甚至各 ...

  5. C语言之网络编程(服务器和客户端)

    Linux网络编程 1. 套接字:源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字.其用于标识客户端请求的服务器和服务. 常用的TCP/IP协议的3种套接字类型如下所示. (1)流套接 ...

  6. 【Android实战】----从Retrofit源代码分析到Java网络编程以及HTTP权威指南想到的

    一.简单介绍 接上一篇[Android实战]----基于Retrofit实现多图片/文件.图文上传中曾说非常想搞明确为什么Retrofit那么屌. 近期也看了一些其源代码分析的文章以及亲自查看了源代码 ...

  7. socket网络编程【python】

    转自:http://www.jb51.net/article/19751.htm socket 是网络连接端点. 一.网络知识的一些介绍 socket 是网络连接端点.例如当你的Web浏览器请求www ...

  8. UNIX网络编程读书笔记:原始套接口

    概述 应用程序可以绕过传输层而直接使用IPv4和IPv6,这称为原始套接口(raw socket).http://www.cnblogs.com/nufangrensheng/p/3583435.ht ...

  9. UNIX网络编程读书笔记:简介

    认知套接口编程接口 理解原始套接口(raw socket)的概念   值得注意的是,客户和服务器是典型的用户进程,而TCP和IP协议则通常是系统内核协议栈的一部分. 上图中在TCP和UDP之间留有间隙 ...

随机推荐

  1. [C++] 类的使用(1)

    1.类的基本思想是数据抽象和封装.数据抽象是一种依赖于接口和实现分离的编程(以及设计)技术. 2.常量对象,以及常量对象的引用或指针都只能调用常量成员函数.因为非常量成员函数有可能修改其作用的对象,与 ...

  2. 遗传编程(GA,genetic programming)算法初探,以及用遗传编程自动生成符合题解的正则表达式的实践

    1. 遗传编程简介 0x1:什么是遗传编程算法,和传统机器学习算法有什么区别 传统上,我们接触的机器学习算法,都是被设计为解决某一个某一类问题的确定性算法.对于这些机器学习算法来说,唯一的灵活性体现在 ...

  3. Spring Boot (五): Redis缓存使用姿势盘点

    1. Redis 简介 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化 ...

  4. 给body设置高度

    今天做一个小demo,需要给body设置高度为100%,然鹅发现只设置body的height不可行,需,html,body{height:100%;}为何呢?一个容器的height是从上一级继承而来的 ...

  5. java项目打包

    http://blog.csdn.net/qq_34845382/article/details/53885907 自己用Rinnable JAR file 方法也可以.更简单.直接点击Finish即 ...

  6. html盒模型基础

    盒模型              概念:如果CSS对HTML文档元素生成了该元素在HTML文档布局中占据空间的矩形元素框(element box),称盒子             通过一系列定义盒子的 ...

  7. 波士顿房价预测 - 最简单入门机器学习 - Jupyter

    机器学习入门项目分享 - 波士顿房价预测 该分享源于Udacity机器学习进阶中的一个mini作业项目,用于入门非常合适,刨除了繁琐的部分,保留了最关键.基本的步骤,能够对机器学习基本流程有一个最清晰 ...

  8. tomcat配置目录及安装说明

    1.升级jdk版本 java -version 查看当前java版本 上传最新版jdk tar xf jdk-8u191-linux-x64.tar.gz 解压jdk到当前下 mv jdk1.8.0_ ...

  9. springboot 整合ehcache缓存

    1.CacheManager Spring Boot默认集成CacheManager,如下包所示: 可以看出springboot自动配置了 JcacheCacheConfiguration. EhCa ...

  10. git 查看分支

    1.查看本地分支 git branch 2.查看所有分支 git branch -a 2.查看所有分支及对应版本信息 git branch -va