本帖最后由 lindabell 于 2017-1-12 16:42 编辑
事情是这样的,我做了一台设备可以通过app控制,也可以上报温湿度等信息的;使用透传的WIFI模块。
设备从关机到开机会发生很多状态变化,都会上传这些状态;通过串口发送到WIFI模块,然后到服务器。
在服务器接收那边就会出现黏包的现象,由于黏包服务器处理起来非常耗时,应该是3~4s的数据到了数据库看居然花了26s左右。
另外我数据的格式是这样的 55AA+MAC+len+CRC8,黏包就是多包数据被TCP封成一个包了。
希望做个服务器 (要专业做服务器的,不是专业的意见不接受)的坛友,给个意见这样的黏包服务器能不能处理,怎样处理?
注:我是做单片机软件的对服务器一点不懂,但是我需要的是专业的回答,另外回答的不错的;可能还会付费咨询更加详细的,报酬方面可谈。
修改:增加到200元话费,高手希望提示一下
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
这种协议我也写过,汽车行业里的一个国标协议。
服务器人能力不行,这个他肯定也搞不出来;最好搞个json给他就满意了。
印象中modbus是靠时间间隔来区分帧的,所以会这样写。
随便写个函数截断就行了
其实不你只是要解决粘包,还要解决一个数据包分两次收到
楼主最近做的事跟我做的类似,我也是做单片机的,但是服务器没人写,我只有自己写了。 我用的是 UDP,设备端用的是 透传的 GPRS DTU, 设备传过来的数据 会发生, 一包分成2包, 或者多包和并在一起。 所以,我这个数据处理也有你这个问题。 不过花了几天时间,问题基本上解决了。
大体思路如下:
1、接收的数据进行初步的判断,同一个IP地址发过来的数据进行连接。
2、将连接后的数据进行识别,也就是最笨的办法,从前往后遍历,遇到包头,就按照协议规则去计算验证码,验证码正确,就把这部分数据提取出来。 然后前面多出来的数据是无效数据, 而后面多出来的数据要留着,需要跟新接收过来的数据包进行连接。
3、对提取出来的数据进行处理。
我的C# 代码如下, 供你参考:(声明一下,我是做单片机的,上位机我非专业人事,所以代码风格很烂,仅供参考)
1、数据处理代码:(处理代码是单独一个线程运行的,所以里面是个大的死循环)
- private void ServiceProcess()
- {
- byte[] PendingDataBuffer = null; //待处理流数据缓存
- IPEndPoint PendingRemote = null; //待处理流数据的来源地址
- while (true)
- {
- //读取并缓存原始数据
- List<UdpReceiveDataStruct> ReceiveOrgList = ServiceUdpClient.GetReceiveData();
- while (ReceiveOrgList.Count > 0)
- {
- UdpReceiveDataStruct ReceiveOrg = ReceiveOrgList[0];
- lock (UdpReceiveOrgListLocker)
- {
- UdpReceiveOrgList.Add(ReceiveOrg);
- ReceiveOrgList.RemoveAt(0);
- }
- }
- //由于原始数据可能存在错误的分包、组包,需要对数据进行识别并缓存有效数据
- while (UdpReceiveOrgList.Count > 0)
- {
- UdpReceiveDataStruct ReceiveOrg;
- //通信总数+1
- lock (CommunicatorStateLocker)
- {
- CommunicatorState.CommunicationTotalCount++;
- }
- //取出一条原始数据
- lock (UdpReceiveOrgListLocker)
- {
- ReceiveOrg = UdpReceiveOrgList[0];
- UdpReceiveOrgList.RemoveAt(0);
- }
- //初步判断数据是否有效,并合并
- if (PendingDataBuffer == null) //待处理流数据为空
- {
- PendingDataBuffer = (byte[])ReceiveOrg.DataContent.Clone();
- PendingRemote = ReceiveOrg.Remote;
- }
- else
- {
- if (PendingRemote.Address == ReceiveOrg.Remote.Address) //判断数据来源是否相同,如果相同,连接数据
- {
- byte[] newBuffer = (byte[])PendingDataBuffer.Clone();
- PendingDataBuffer = newBuffer.Concat(ReceiveOrg.DataContent).ToArray();
- PendingRemote = ReceiveOrg.Remote; //为防止来源地址其他属性发生变化,进行更新
- }
- else //如果不同,丢弃原来的数据,并把丢弃的数据通过Log输出
- {
- //输出Log
- string str = "【" + ProjectSetting.ProjectCode + "】" + "NWGY接收数据出现无效数据段:" + PendingRemote.Address.ToString() + ":" + PendingRemote.Port.ToString() + ",";
- foreach (byte d in PendingDataBuffer)
- {
- str += d.ToString("X2") + " ";
- }
- Log.WriteLine(str);
- //通信错误数+1
- lock (CommunicatorStateLocker)
- {
- CommunicatorState.CommunicationErrorCount++;
- }
- PendingDataBuffer = (byte[])ReceiveOrg.DataContent.Clone();
- PendingRemote = ReceiveOrg.Remote;
- }
- }
- //将初步处理的数据,进行识别
- while (true) //进行多次识别,直到不存在有效数据
- {
- if (PendingDataBuffer == null)
- {
- break;
- }
- //识别连接后的数据,输出有效数据、无效数据、未完成数据
- //有效数据存入
- DataIdentResultStruct DataIdentResult = HelperNWGY10Encoding.IdentData(PendingDataBuffer);
- if (DataIdentResult == null)
- {
- break;
- }
- if (DataIdentResult.InvalidData != null) //无效数据通过LOG输出
- {
- string str = "【" + ProjectSetting.ProjectCode + "】" + "NWGY数据识别出现无效数据段:" + PendingRemote.Address.ToString() + ":" + PendingRemote.Port.ToString() + ",";
- foreach (byte d in PendingDataBuffer)
- {
- str += d.ToString("X2") + " ";
- }
- Log.WriteLine(str);
- //通信错误数+1
- lock (CommunicatorStateLocker)
- {
- CommunicatorState.CommunicationErrorCount++;
- }
- }
- PendingDataBuffer = DataIdentResult.IncompleteData; //未完成数据进行缓存,以便进行下一次识别
- if (DataIdentResult.ValidData != null) //存在有效数据,进行缓存,并进行下一次识别,否则跳出识别循环
- {
- UdpReceiveDataStruct ReceiveData = new UdpReceiveDataStruct();
- ReceiveData.Remote = PendingRemote;
- ReceiveData.DataContent = (byte[])DataIdentResult.ValidData.Clone();
- lock (UdpReceiveDataListLocker)
- {
- UdpReceiveDataList.Add(ReceiveData);
- }
- }
- else
- {
- break;
- }
- }
- }
- //这里是对识别好的数据进行处理的部分了,删去了。
- ...
- //数据处理部分结束
- if (ServiceStopFlag == true)
- {
- break;
- }
- //挂起一下,防止CPU利用率过高
- Thread.Sleep(10);
- }
- }
复制代码2、处理处理代码中使用的数据识别提取函数
- public static DataIdentResultStruct IdentData(byte[] Data)
- {
- if (Data == null)
- {
- return (null);
- }
- DataIdentResultStruct DataIdentResult = new DataIdentResultStruct();
- byte[] OrgData = (byte[])Data.Clone();
- //查找数据包起始码
- for (int i = 0; i < OrgData.Length; i++)
- {
- if (OrgData[i] == 0x68)
- {
- //找到一个起始码,计算剩下的数据长度是否可能构成一个数据包
- if (i + 12 > OrgData.Length) //数据长度不够
- {
- if (i > 0)
- {
- DataIdentResult.InvalidData = OrgData.Take(i).ToArray();
- }
- DataIdentResult.IncompleteData = OrgData.Skip(i).ToArray();
- return DataIdentResult;
- }
- //根据协议计算剩下的数据长度是否可构成一个数据包
- int DataSegLenth = OrgData[i + 8] * 256 + OrgData[i + 9];
- int PacketLenth = 12 + DataSegLenth;
- if(i+ PacketLenth> OrgData.Length) //数据长度不够
- {
- if (i > 0)
- {
- DataIdentResult.InvalidData = OrgData.Take(i).ToArray();
- }
- DataIdentResult.IncompleteData = OrgData.Skip(i).ToArray();
- return DataIdentResult;
- }
- //判断数据包结束码
- if (OrgData[i + PacketLenth - 1] != 0x16) //结束码不正确,继续循环
- {
- continue;
- }
- //计算SUM是否正确
- if (SumCalc(OrgData, i + 1, PacketLenth - 3) != OrgData[i + PacketLenth - 2]) //SUM不正确,继续循环
- {
- continue;
- }
- //能运行至此,说明数据包是正确的
- if (i > 0)
- {
- DataIdentResult.InvalidData = OrgData.Take(i).ToArray();
- }
- DataIdentResult.ValidData = OrgData.Skip(i).Take(PacketLenth).ToArray();
- if (i + PacketLenth < OrgData.Length)
- {
- DataIdentResult.IncompleteData = OrgData.Skip(i + PacketLenth).ToArray();
- }
- return DataIdentResult;
- }
- }
- //如果循环执行到结束了,说明没找到有效的数据包,所有的数据均为未完成数据
- DataIdentResult.IncompleteData = (byte[])OrgData.Clone();
- return DataIdentResult;
- }
复制代码一周热门 更多>