这段时间用STM32移植LwIP做语音传输。但是遇到一个问题困扰许久,在使用TCP方式做一个client去连接server,由于数据量比较大经常在连接一个多小时候就出现断线而

也ping不通。接下来我们看一下这个问题是怎么出现的和他的决绝方法(小白一枚,说错的地方还望指正哈 。。。。共同学习 。嘻嘻 ^_^  )。

额,还没有学操作系统,还生活在裸奔的年代。。。 client和server采用LwIP的Raw函数编写。连接过程采用短连接,即发送一次数据就请求断开。

我们先看一个client端的程序。

/*

**创建一个连接

*/

voidclient_init(void)

{

#define server_point 1080 //server端口号

structtcp_pcb *Clipcb;//创建一个pcb控制块

structip_addr ipaddr;//IP

IP4_ADDR(&ipaddr,192,168,1,18);  //server地址

Clipcb = tcp_new(); ;//分配一个控制块

if(Clipcb != NULL)

tcp_connect(Clipcb,&ipaddr,server_point,TcpCli_Connected); //创建连接 并注册连接成功的回调函数 Tcp_Cli_Connected

}

如果创建成功那么就进入回调函数

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

{

tcp_write(tpcb,(void *)sound_buf[NT_USE_BUF_NUM],sizeof(sound_buf[ NT_USE_BUF_NUM]),0); //发送数据

tcp_close(tpcb);  //关闭连接

return ERR_OK;

}

在主函数中调用发送数据

int main(void)

{

do somthing

if(发送条件成立)

{

client_init();

}

do something

好,那么问题来了。上面这种方法在数据量比较小的时候基本上不会出现什么问题,但是在数据量比较大的时候就会出现协议栈卡死。而出现上述问题的关键是TCP连接的时候如果要断开一个连接要经过4次握手,而我们在断开连接的时候只是简单的调用了 tcp_close( )函数,然而它具体的调用结果是什么了,我们没有进行检测。额这里就来看一下tcp_close函数。

err_t

tcp_close(struct tcp_pcb *pcb)

{

err_t err;

#if TCP_DEBUG

LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));

tcp_debug_print_state(pcb->state);

#endif /* TCP_DEBUG */

switch (pcb->state) {  //判断pcb控制块的连接状态

case CLOSED:

err = ERR_OK;

TCP_RMV(&tcp_bound_pcbs, pcb);  //将pcb控制块移除绑定链表

memp_free(MEMP_TCP_PCB, pcb);  //释放pcb控制块

pcb = NULL;

break;

case LISTEN:

err = ERR_OK;

tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb);//将pcb控制块移除监听队列

memp_free(MEMP_TCP_PCB_LISTEN, pcb);

pcb = NULL;

break;

case SYN_SENT:

err = ERR_OK;

tcp_pcb_remove(&tcp_active_pcbs, pcb); //移除活动队列

memp_free(MEMP_TCP_PCB, pcb);

pcb = NULL;

snmp_inc_tcpattemptfails();

break;

case SYN_RCVD:

err = tcp_send_ctrl(pcb, TCP_FIN);  //发送FIN关闭请求

if (err == ERR_OK) {

snmp_inc_tcpattemptfails();  //一个宏定义没有看到函数体

pcb->state = FIN_WAIT_1;

}

break;

case ESTABLISHED:

err = tcp_send_ctrl(pcb, TCP_FIN); //发送FIN关闭请求

if (err == ERR_OK) {

snmp_inc_tcpestabresets();  //宏

pcb->state = FIN_WAIT_1;

}

break;

case CLOSE_WAIT:

err = tcp_send_ctrl(pcb, TCP_FIN); //发送FIN

if (err == ERR_OK) {

snmp_inc_tcpestabresets();

pcb->state = LAST_ACK;

}

break;

default:      //其他状态认为连接已经关闭

/* Has already been closed, do nothing. */

err = ERR_OK;

pcb = NULL;

break;

}

if (pcb != NULL && err == ERR_OK) {

tcp_output(pcb); //将没有发送的数据发送出去

}

return err;

}

在分析这段代码之前我们先看一下pcb->state 是什么。它是pcb结构体的一员,他主要使用来记录pcb控制块的状态他总共有11中状态,在这里我把他分为了两类(他的所有状态都可以在tcp.c 这个源文件中找到)

第一类、处于连接状态 或者说是pcb控制块没有释放的状态吧:

CLOSED:没有任何连接状态

LISTEN:侦听来自远方的TCP端口的连接请求

SYN-SENT:再发送连接请求后等待匹配的连接请求

SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认

ESTABLISHED:代表一个打开的连接

CLOSE-WAIT:等待从本地用户发来的连接中断请求

第二类、连接关闭状态(tcp -> state)

FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认

FIN-WAIT-2:从远程TCP等待连接中断请求

CLOSING:等待远程TCP对连接中断的确认

LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认

TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认

还有一个虽然不是pcb->state 但是当pcb == NULL的时候也是处在没有连接的状态

了解了上面的以后我么就能够判断一个pcb控制块处于什么状态了。那么我们开始的那种创建连接的问题出在哪里了,在我们创建了一个连接的时候要经过3次握手,那么可能在某一连接中client发送出SYN以后对方还没有回复ACK正式建立连接我们就有创建了一个新的pcb那么在数据量比较大的时候(这次做音频应该是32K*4bps吧)就有一次的创建了一个pcb控制块,或者是在我们的回掉函数中没有正确关闭pcb连接。那么这个连接韩式存在而我们后续却没有对他进行操作。如果在应用层一直没有再次调用tcp_close()函数那么这个连接将一直存在,可见是一个灾难性的结果,最终协议栈资源耗光、挂掉了。

那么我们的目标很明确就是在每次创建一个新的连接的时候确保上一个连接已经关闭,于是我们在创建连接以后就注册一个poll函数,在我们上面的TcpCli_Connected()函数调用tcp_poll()函数注册一个用户轮询函数client_poll()在他里面关闭连接,在这里注册就保证了是已经正常接通的连接,同时回调函数会隔一段时间去调用。这样如果我们没有正常关闭连接就会调用用这个poll来关闭,对应的如果正常关闭了那么就不会再调用这个回调函数。然后第二个注意的就是在每次创建连接的时候都要判断当前连接的状态。第三就是在server函数中也注册一个回调函数用来关闭连接。

Clipcb是一个全局变量

/*

**创建一个连接

*/

void client_init(void)

{

#define server_point 1080

struct ip_addr ipaddr;

if(Code_Void_BA

{

IP4_ADDR(&ipaddr,192,168,1,180);  //server IP

/* add according to https://lists.gnu.org/archive/html/lwip-users/2012-06/msg00031.html*/

/*确保上一次连接正确关闭*/

if( (Clipcb == NULL ) || (Clipcb->state ==FIN_WAIT_1) || (Clipcb->state == TIME_WAIT)||(Clipcb->state == CLOSED)

||(Clipcb->state == CLOSING)||(Clipcb->state == FIN_WAIT_2)||(Clipcb->state ==LAST_ACK))

{

if(Clipcb->state == CLOSED)

{

tcp_close(Clipcb);

}

Clipcb = tcp_new();

if(Clipcb == NULL)

{

printf("malloc pcb err!\n");

}

else

{

tcp_connect(Clipcb,&ipaddr,server_point,TcpCli_Connected);//创建连接

}

}

NT_USE_BUF_NUM = (NT_USE_BUF_NUM+1)%CodeBufAmount; //切换缓冲区

Code_Void_BA++;

}

}

成功连接后的回调函数,client_poll函数就是直接关闭连接就OK了

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

{

static uint32_t ti = 0;

Clipcb = tpcb;

tcp_poll(Clipcb, client_poll, 0); //注册回调函数

tcp_write(tpcb,(void *)cod_buf[NT_USE_BUF_NUM],sizeof(cod_buf[ NT_USE_BUF_NUM]),0); //将数据填写到发送队列

if(ERR_OK != tcp_close(tpcb))

{

sta = 1; printf("close err!\n");

}

else  sta = 0;

return ERR_OK;

}

KO

lwip连续发数据卡死_LwIP用TCP连接方式在数据量比较大协议栈卡死相关推荐

  1. 2022.9.07 TCP协议特点,TCP适用场合,TCP连接方式(三次握手,四次挥手)。

    传输层协议: TCP   <传输控制协议>    transport control protocol TCP协议特点: 是一种面向连接的传输层协议,它能够提供高可靠性通信,无数据丢失,无 ...

  2. MobileIMSDK怎样修改Server端和安卓端TCP连接方式时报文的的限制大小

    场景 MobileIMSDK怎样修改服务端核心jar包的源码并替换掉Java服务端的jar包: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/de ...

  3. lwip连续发数据卡死_用lwip发送大量数据时,遇到的问题解答记录;

    这两天,师弟在两台电脑上搭建了lwipwin32通信平台,目的是能够不断发送一幅幅图片图像大小为1280*720大小的图片.如果不考虑压缩情况且是256色即1字节,这样大小的一幅图片需要发送大约发送1 ...

  4. Wireshark抓包分析TCP连接、发送数据与断开过程

    准备工具: 1. 两台连接到同个局域网的电脑,或者虚拟机; 2. 在其中一台电脑安装Wireshark; 3. 在两台电脑上面都有TCP&UDP测试工具软件 TCP连接建立过程(三次握手): ...

  5. gprs模块发送html,GPRS模块通过TCP/IP方式发送数据流程

    首先介绍一下GPRS的接入方式,在中国有CMWAP和CMNET两种方式.CMWAP 和 CMNET 只是中国移动人为划分的两个GPRS接入方式.前者是为手机WAP上网而设立的,后者则主要是为PC.笔记 ...

  6. FTP的PORT和PASV的连接方式以及数据连接端口号计算

    PORT(主动)方式的连接过程是: 客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路. 当需要传送数据时,客户端在命令链路上用 PORT命令告诉服务器:" ...

  7. lwip连续发数据卡死_Mysteel:12月全球铁矿石发运量稳中微增 进口矿咋走?

    十一月份铁矿石供需差收紧价格上行.展望十二月份, 全球铁矿石发运稳中微增,到港小幅下降,澳洲方面,力拓财年末冲量将带来部分增量,但BHP仍受检修影响环比或将有所减量,整体来看,预计澳洲发运量环比增加: ...

  8. lwip连续发数据卡死_用lwip发送大量数据时,遇到的问题解答记录 | 求索阁

    1.概述: lwip是轻量型的TCP/IP实现,只需10几KB的RAM和40几KB的ROM就能够跑起来,适合应用于嵌入式设备的网络通信.有牛人Adam Dunkels发明,提供给用户上那种接口 RAW ...

  9. C#调用斑马打印机打印条码标签(支持COM、LPT、USB、TCP连接方式和ZPL、EPL、CPCL指令)

    在批量打印商品标签时一般都要加上条码或图片,而这类应用大多是使用斑马打印机,所以我也遇到了怎么打印的问题. 一种办法是用标签设计软件做好模板,在标签设计软件中打印,这种办法不用写代码,但对我来说觉得不 ...

最新文章

  1. 深度学习博士发出灵魂拷问:我是在做算法还是在调参?
  2. Python matplotlib可视化:在Matplotlib中为坐标轴刻度添加自定义符号(例如,货币符号¥$等)、水平条形图(horizontal bar)
  3. linux 图形界面 x x11 gnome xorg kde 之间的关系
  4. 关于小型长周期项目的一些建议
  5. python面向对象(part1)--类和对象
  6. DEV-C上的报错 Process exited after 4.03 seconds with return value 3221225725
  7. nacos启动失败:org.springframework.boot.web.server.WebServerExceptio
  8. 小程序 delete_程序员崩溃的43个瞬间!搞工科的是不是都这样?
  9. matlab与python实现神经网络_Adaline神经网络简单介绍和MATLAB简单实现
  10. android service 样例(电话录音和获取系统当前时间)
  11. 2018,如何从技术小白升级到大牛程序员?
  12. 使用k8s Ingress暴露gRPC服务
  13. apt update时出现签名无法验证,公钥失效的解决办法
  14. 香港各个大学计算机类专业
  15. ios审核提示:您的 Apple Developer Program 帐户已被标记为删除
  16. 阿里云服务器续费流程及折扣
  17. portraiture4图片修图磨皮滤镜插件支持Win和Mac
  18. OSChina 周日乱弹 —— 我女朋友都放到购物车里都没钱买
  19. 使用计算机搭建防火墙,电脑防火墙在哪里设置?电脑防火墙设置方法介绍
  20. tcp 为什么要三次握手 两次不行吗

热门文章

  1. flink离线mysql_Flink 流模式跑离线任务
  2. python抽奖简单小程序游戏_python——(分别用两种方式实现)公司年会抽奖小程序...
  3. 圣诞节海报设计还没开始?感受下合适的节日感PSD模板
  4. java并发包作者lee_Java的一些并发包
  5. Linux协议栈:基于ping流程窥探Linux网络子系统,及常用优化方法
  6. 为什么你的发行版仍然在使用“过时的”Linux 内核? | Linux 中国
  7. BIO,NIO和AIO的区别
  8. python自顶向下设计步骤_python自底向上的执行单元测试
  9. pytorch dataloader_基于pytorch的DeepLearning入门流程
  10. 32 配置引脚中断_PCIe的中断机制