最近在LPC1768上调试uIP1.0的网络协议代码,设置配置的是不使用分包发送的模式,原本想着发送回传的字节数应该也不会太大,我都是本地自定义的协议.后面调试的时候,发现TCP协议居然有粘包和拆包的问题

其中粘包的问题最为恼火,本来这个轻协议栈已经出来很多年了,现在已经又很多人发现不玩这种嵌入式的东西,想找一个uIP协议栈的主动发送都没找到,最后只有在阿莫论坛上找到唯一一片稍微有价值的文章

因为是在设备端使用uIP的TCP服务器配置,上位机连接到设备后发送指定协议,但是居然发现在设备端的TCP服务器不能实现主动发送!!!!这就很烦恼了.本来一个双工通讯的,活生生的搞成单工

问题是这样的

1. UI端发送14字节包请求到设备,设备回复56字节,设备的uIP缓存是56*10字节,也就是说单次uIP最多就只能回复10个帧.那么如果TCP发送粘包了,,比如突然来了20个14字节请求,那么一次设备就响应不了!!!!!问题就来,需要回复20个包的时候uIP没这么缓存了,最大只能回复10个,那么剩下的10个怎么办???

2.解决上面的办法就是在设备端进行ack响应,先回复10个包给UI,UI这个时候会回复一个ACK给设备,设备收到ACK后再处理剩下的包.

3.好,按照上面的思路做好了,现在又有一个问题,就是UI端发送14字节请求是一个不断的过程,那么这就有可能UI收到第一次的10个包的时候,第二次正好要发14字节,那么TCP协议就会把这个ACK Flag放到14字节的TCP协议上进行一起发送给设备(这样设备就无法确定这次是要处理ACK还是newdata),正常是应该需要处理先处理ack的包,处理返回完了,再处理newdata,这样就是要求设备端把接收到的数据作为一个缓存存起来,.这样坐一个循环缓存还是有问题,就是当UI发送快了之后,设备端还是会存在缓存满的情况!

4.忽然想到另外一个思路

因为uIP现在默认的接收缓存和发送缓存都是同一段内存,那么我在这里做一个区分,接收缓存设置14*4 * 2字节,返回的缓存设置成56*8字节,这种情况下我是不是就可以保证在网络上是可以对称通讯的呢?因为设备的接收缓存,始终都不会超过14*4 * 2字节接收,这样处理返回的值就是56*4*2字节!!

晚上再调试代码测试一下这个思路如何?可行的话后续再更新

2022年2月23日更新一下

上面的思路还是有问题,如果uIP的发送缓存和接收缓存设置成不同的内存段,我发现需要改动的内容还不少,最后的解决办法是调整上位机UI端TCP发送速度,原来是13ms一次,后面改成20ms我发现粘包的概率就大大降低了,同时配合ACK多次返回的规则写代码这样粘包基本能处理了

这个问题至此,我已经在48SP处理器项目上使用了,后续准备测试验收

最后上一段这部分调试的代码

非常核心!!!!全是多年工作的精华


//TCP回调函数:处理网络来的数据
void TCP_Server_AppCall(void)
{int i = 0;//串口数据处理,g_u8Uart0RxDataMem是运行到这函数里面时接收到数据的总缓存unsigned char *pUart0RxDataMem = (unsigned char * )g_u8Uart0RxDataMem;unsigned char u8UartParaBuf[UART0_FRAME_MAX_LEN] = {0};     //临时变量,每次处理一帧数据unsigned char u8DataLen = 0;unsigned short u16TempRxStartIndex = 0;if(pUart0RxDataMem == NULL){return ;}int nLen = 0;unsigned char *pUIPData = (unsigned char * )uip_appdata;unsigned short u16UIPDataSendLen = 0;unsigned short u16UIPOneFrameOutLen = 0;if(uip_connected()){guIPRxByteNum = 0;    //首次连接}if(uip_newdata())   //收到服务器发来数据 数据存在uip_appdata[]数组{nLen = gEmacTcpPocketLen;    //gEmacTcpPocketLen随着每次接收会变化pUIPData = (unsigned char * )uip_appdata;if(nLen <= 0)       //如果接受到数据小于0,不处理{nLen = nLen + 1;   //调试用return ;}// uip_newdata 新数据存入g_u8uIPDataBuff缓存中,采用逐个拷贝的方式for(i = 0; i < nLen; i++){pUart0RxDataMem[gUart0RxIndex % UART0_RX_BUFF_LEN] = pUIPData[i];gUart0RxIndex = (gUart0RxIndex + 1) % UART0_RX_BUFF_LEN;guIPRxByteNum++;    //接收到的个数加1if(guIPRxByteNum > UART0_RX_BUFF_LEN)     //数据太多没处理完{guIPRxByteNum = guIPRxByteNum;}}//开始解析数据while(guIPRxByteNum >= 3){while(guIPRxByteNum >= 3)     //如果接收到的数据长度大于3个字节,就可以进行帧头0x55AA的判断{//用桢头完整字节判断,一个字节容易出错if((pUart0RxDataMem[gUart0RxStartIndex] == 0x55) &&(pUart0RxDataMem[(gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN] == 0xAA)){u8DataLen = pUart0RxDataMem[((gUart0RxStartIndex + 2) % UART0_RX_BUFF_LEN)];//第3个字节时本帧的长度,取出来判断一下是不是14或者56if((UART_RX_PROTOCOL_SIZE == u8DataLen) ||(UART_RX_ExPROTOCOL_SIZE == u8DataLen) )    //检测到指定的协议长度就结束循环{break;}}//如果帧长不等于14或者56,那么把帧头的第一个字节置0,去掉一个字节,再循环判断,直到检测到0x55AA就跳出循环pUart0RxDataMem[gUart0RxStartIndex % UART0_RX_BUFF_LEN] = 0;gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;  //gUart0RxStartIndex的大小始终都在0~UART0_RX_BUFF_LEN之间,防止溢出guIPRxByteNum--;          //接收到的总字节数减掉1guIPRxByteNum = ( guIPRxByteNum <= 0) ? 0 : guIPRxByteNum;    //防接收负数处理}//执行到这里,表示检测到了0x55AAif((guIPRxByteNum >= u8DataLen) && (u8DataLen > 0))     //如果接收的字节数大于协议中的帧长{//先把数据取出(gUart0RxStartIndex读取的偏置先不变)做校验u16TempRxStartIndex = gUart0RxStartIndex;for(i = 0; i < u8DataLen; i++){u8UartParaBuf[i] = pUart0RxDataMem[u16TempRxStartIndex];//校验通过将数据取出存放到u8UartParaBufu16TempRxStartIndex = (u16TempRxStartIndex + 1) % UART0_RX_BUFF_LEN;}if(CheckReciveDataCRC(u8UartParaBuf, u8DataLen))//如果校验正确即可从原大缓存中清除对应的字节数后按照协议提取数据{//怎么判断这是一个有效处理还是无效处理u16UIPOneFrameOutLen = ProcessingProtocolData(u8UartParaBuf, u8DataLen);if(NETWORK_RETURN_BUFLEN - u16UIPDataSendLen >= u16UIPOneFrameOutLen){//拷贝送到uIP缓存,与串口不一样的地方在这里memcpy(&pUIPData[u16UIPDataSendLen], u8UartParaBuf, u16UIPOneFrameOutLen * sizeof(unsigned char));u16UIPDataSendLen += u16UIPOneFrameOutLen;guIPOutFrameCount++;for(i = 0; i < u8DataLen; i++)      //清除取出的数据{pUart0RxDataMem[gUart0RxStartIndex] = 0;gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;guIPRxByteNum--;guIPRxByteNum = ( guIPRxByteNum <= 0) ? 0 : guIPRxByteNum;    //防接收负数处理}}else{//数据到这里了表示返回内存已满,不能再存了uip_send(pUIPData, u16UIPDataSendLen);return;}}else{//如果校验错误,前面的数据已经被清除为0,因此这桢数据就丢弃pUart0RxDataMem[gUart0RxStartIndex] = 0;    //只需清除一个字节让while循环去清除其他数据gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;guIPRxByteNum--;guIPRxByteNum = ( guIPRxByteNum <= 0) ? 0 : guIPRxByteNum;    //防接收负数处理}}}uip_send(pUIPData, u16UIPDataSendLen);}//2022.02.19使用ACK作为判断还是有BUG,因为当UI上位机发送数据过快粘包时未来得及处理的包返回缓存依旧超过//这样新数据中会带有ACK标志位,这样既需要处理上一次的数据,也需要处理newdata,会造成丢包if(uip_acked())    //接收到回应表明上一个数据包发送完成,开始处理下一个包{//开始解析数据while(guIPRxByteNum >= 3){while(guIPRxByteNum >= 3)     //如果接收到的数据长度大于3个字节,就可以进行帧头0x55AA的判断{//用桢头完整字节判断,一个字节容易出错if((pUart0RxDataMem[gUart0RxStartIndex] == 0x55) &&(pUart0RxDataMem[(gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN] == 0xAA)){u8DataLen = pUart0RxDataMem[((gUart0RxStartIndex + 2) % UART0_RX_BUFF_LEN)];//第3个字节时本帧的长度,取出来判断一下是不是14或者56if((UART_RX_PROTOCOL_SIZE == u8DataLen) ||(UART_RX_ExPROTOCOL_SIZE == u8DataLen) )    //检测到指定的协议长度就结束循环{break;}}//如果帧长不等于14或者56,那么把帧头的第一个字节置0,去掉一个字节,再循环判断,直到检测到0x55AA就跳出循环pUart0RxDataMem[gUart0RxStartIndex % UART0_RX_BUFF_LEN] = 0;gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;  //gUart0RxStartIndex的大小始终都在0~UART0_RX_BUFF_LEN之间,防止溢出guIPRxByteNum--;          //接收到的总字节数减掉1guIPRxByteNum = ( guIPRxByteNum <= 0) ? 0 : guIPRxByteNum;    //防接收负数处理}//执行到这里,表示检测到了0x55AAif((guIPRxByteNum >= u8DataLen) && (u8DataLen > 0))     //如果接收的字节数大于协议中的帧长{//先把数据取出(gUart0RxStartIndex读取的偏置先不变)做校验u16TempRxStartIndex = gUart0RxStartIndex;for(i = 0; i < u8DataLen; i++){u8UartParaBuf[i] = pUart0RxDataMem[u16TempRxStartIndex];//校验通过将数据取出存放到u8UartParaBufu16TempRxStartIndex = (u16TempRxStartIndex + 1) % UART0_RX_BUFF_LEN;}if(CheckReciveDataCRC(u8UartParaBuf, u8DataLen))//如果校验正确即可从原大缓存中清除对应的字节数后按照协议提取数据{u16UIPOneFrameOutLen = ProcessingProtocolData(u8UartParaBuf, u8DataLen);if(NETWORK_RETURN_BUFLEN - u16UIPDataSendLen >= u16UIPOneFrameOutLen){//拷贝送到uIP缓存,与串口不一样的地方在这里memcpy(&pUIPData[u16UIPDataSendLen], u8UartParaBuf, u16UIPOneFrameOutLen * sizeof(unsigned char));u16UIPDataSendLen += u16UIPOneFrameOutLen;guIPOutFrameCount++;for(i = 0; i < u8DataLen; i++)      //清除取出的数据{pUart0RxDataMem[gUart0RxStartIndex] = 0;gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;guIPRxByteNum--;guIPRxByteNum = ( guIPRxByteNum <= 0) ? 0 : guIPRxByteNum;    //防接收负数处理}}else{//数据到这里了表示返回内存已满,不能再存了uip_send(pUIPData, u16UIPDataSendLen);return;}}else{//如果校验错误,前面的数据已经被清除为0,因此这桢数据就丢弃pUart0RxDataMem[gUart0RxStartIndex] = 0;    //只需清除一个字节让while循环去清除其他数据gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;guIPRxByteNum--;guIPRxByteNum = ( guIPRxByteNum <= 0) ? 0 : guIPRxByteNum;    //防接收负数处理}}}uip_send(pUIPData, u16UIPDataSendLen);}}

uIP1.0 主动发送的问题理解相关推荐

  1. UDP协议报文分析和主动发送UDP简单实现

    UDP协议报文分析和主动发送UDP简单实现 前言 一.Wireshark 报文解析 1.UDP报文实例 2.报文格式分析 ①.以太网头 ②.IP头 ③.UDP头 二.UDP主动传输数据的实现方式 1. ...

  2. uip1.0核心模块uip_process函数解读

    转载地址:https://www.amobbs.com/thread-5531817-1-1.html 最近,利用uip搞了小东西,要想利用好uip, 最好彻底搞清楚其关键函数 uip_process ...

  3. uip协议,主动发送功能

    一.uip协议,作为服务器 一般情况下,在uip协议作为服务器使用,在while(1)中实现以下代码,不断读取网卡数据,进行处理,然后返回给客户端. uip_len = tapdev_read((vo ...

  4. 微信企业号开发:主动发送消息

    主企业号主动发送消息,也就是企业号主动推送的消息,适合于企业的通知,通告等.因此如果公司有通知,要求通知到所有员工,就应该使用主动发送消息. 格式是json格式,而且微信很灵活,当touser,top ...

  5. php libev 主动发送,libev学习笔记

    之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅显易懂的语言帮助大家做一个入门,我自己也是入门程序媛一只,所以有理解错误的地方 ...

  6. stm32f103C8T6移植enc28j60+UIP1.0(吐槽篇)

    移植环境(蓝色粗体字为特别注意内容) 1,开发板:STM32F103C8T6最小系统开发板. 2,开发环境:Keil uv5 3,参考文献:https://blog.csdn.net/wzs298/a ...

  7. 基于MySQL 8.0 对事务的深度理解

    基于MySQL 8.0 对事务的深度理解 一.MySQL中事务隔离级别 事务的隔离级别有哪些? 隔离级别 脏读 不可重复读 幻读(虚读) 未提交读(Read uncommitted) 可能 可能 可能 ...

  8. EJB3.0定时发送jms(发布/定阅)方式

    EJB3.0定时发送jms(发布/定阅)方式 介绍: 定时器分为两种:single-action Timer(单动定时器)和interval  Timer(间隔定时器). 为了使用定时服务,1.ent ...

  9. android辅助功能实现群发,Android 8.0实现发送通知的方法

    Android 8.0实现发送通知的方法 发布时间:2020-07-30 09:34:40 来源:亿速云 阅读:111 作者:小猪 这篇文章主要讲解了Android 8.0实现发送通知的方法,内容清晰 ...

最新文章

  1. 3人小公司1年的创业成本
  2. 艾伟_转载:.NET设计模式:观察者模式(Observer Pattern)
  3. 一个学渣三次面试阿里之路
  4. HTML5 progress和meter控件
  5. 【收藏】华为路由器交换机配置命令大全
  6. mysql忽略列,MySQL:使用DISTINCT时忽略选定的列
  7. 关于继承方式和访问权限
  8. 一般处理程序制作的验证码
  9. 【译】 Intelligent Agent Based RFID Wireless Body Sensor Mesh Network
  10. 连接思科无线经常出现获取不到地址_思科(cisco)路由器登录IP地址默认密码说明...
  11. 【交易技术前沿】低时延基础设施杂谈
  12. BDS和GPS、电离层相关SSR数据解码
  13. kettle的图形工具(Spoon)简单介绍
  14. 软实力:权力,从硬实力到软实力
  15. win10怎么设置护眼背景
  16. vb.net操作Excel常用命令
  17. 手势操作各个方法的含义
  18. body软件 human_人体系统女性3D软件破下载|人体系统女性3d专业中文版下载v1.0(Human body female)-乐游网安卓下载...
  19. 天选2出现找不到wlan问题
  20. android开发图案解锁,Android开发中图案解锁完整版

热门文章

  1. 思王弗(写测试类注释时突发奇想)
  2. 电容的原理与应用(补充中)
  3. 汉高2019年第三季度销售额增长0.8%,达50.77亿欧元
  4. MySQL的COUNT语句暗藏玄机
  5. JS将unicode码转中文方法(解决IE8对JSON.stringify中文转换成unicode的问题)
  6. windows引导过程以及多系统引导原理
  7. vs为什么打了断点不断_2019年亚洲羽毛球锦标赛决赛焦点对阵:桃田贤斗VS石宇奇 松本麻佑/永原和可那VS陈清晨/贾一凡 山口茜VS何冰娇...
  8. 低压差线性稳压器(LDO)设计与仿真
  9. python实训报告pygame_20192423 2019-2020-2 《Python程序设计》实验四报告
  10. 【信捷PLC XL5E-16T试用-程序上传下载】