c# supersocket 自定义协议 错误处理 粘包处理

bianchao1 2017-03-25 10:42:56
请教一个问题。
我使用FixedHeaderReceiveFilter(头部格式固定并且包含内容长度的协议)时
如果硬件设备发送的信息 不符合我自定义协议格式,会导致后面正确数据无法解析。

正确协议格式 如下

0c 03 02 ff ff 94 35
地址 命令 长度 数据(2) CRC(2)

头部为3个字节,DTUReceiveFilter() : base(3),根据头部3个字节中最后一个字节“长度”获取头部后面的数据内容和CRC

如果设备发来了错误格式信息 aa (只有一个字节),过了一会又发来了格式正确的信息,
那么就会变成 aa 0c 03 02 ff ff 94 35

后面数据的解析会一直受影响
请问怎么解决。

硬件设备使用的modbus rtu协议,接收数据时,
会根据时间间隔(3.5个字符的静止时间,大概几毫秒吧)区分每条信息的起始。
超过3.5个字符的静止时间后,会认为是下一条新信息。

下面贴一下代码

public class DTUReceiveFilter : FixedHeaderReceiveFilter<DTURequestInfo>
{
public DTUReceiveFilter()
: base(4)
{

}

/// <summary>
/// 获取数据域和结尾字节长度
/// </summary>
/// <param name="header"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <returns></returns>
protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
{
//length为头部(包含1字节的length数据长度)长度

int len = (int)header[offset + length - 1];//length只有一个字节
return len + 2;//结尾有2个字节CRC
}

/// <summary>
/// 实现帧内容解析
/// </summary>
/// <param name="header"></param>
/// <param name="bodyBuffer"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <returns></returns>
protected override DTURequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
{
DTURequestInfo res = new DTURequestInfo();

string entireFrame = CRC.BytesToHexStr(header.Array) + CRC.BytesToHexStr(bodyBuffer.CloneRange(offset, length));
res.EntireFrame = entireFrame;
res.DTUDeviceID = entireFrame.Substring(0, 2);
res.TerminalDeviceAddress = entireFrame.Substring(2, 2);
res.ControlCode = entireFrame.Substring(4, 2);
res.Length = entireFrame.Substring(6, 2);
int dataLen = int.Parse(CRC.HEXtoDEC(CRC.ReverseHexString(res.Length)));
res.DataContent = entireFrame.Substring(8, dataLen * 2);
res.CRC = entireFrame.Substring(8 + dataLen * 2, 4);
return res;
}

}

...全文
2142 10 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
bianchao1 2019-03-05
  • 打赏
  • 举报
回复
应该有一个有限容量的存储器(可能是环形的),存储收到的消息。
来消息时,会触发事件,在存储器里内容的开头套用协议格式,匹配数据成功后,将数据在存储器中删除。
如果匹配失败,应该清空存储器,断开连接,
或者无动作(但要判断存储器内容长度,在溢出之前清空存储器并断开连接)。

后来用了GatewayWorker,在新消息触发事件时,用正则遍历存储器内容(从头到尾遍历每一个字节,作为正则匹配目标字符串的开始位置),来匹配协议,如果无匹配,清空存储器,断开tcp连接。输出一下,看看收到了什么玩意,转ASCII后像是http协议头,应该是被网站漏洞扫描软件关照了。
bianchao1 2018-04-06
  • 打赏
  • 举报
回复
FixedHeaderReceiveFilter这种协议是很怕数据错误的,尤其是错位,由于干扰或信号质量差等问题,产生了错误数据,数据传到服务器在缓存堆积,当达到length时(FixedHeader中的header的第三个字节,此处跟用户协议有关),产生message相应事件(把数据从缓存取出来),此时要对message进行校验(比如crc16),错误的话,记录日志然后close掉socket。 总之就是收到数据,先校验,错误就断开socket链接,等着下次连接。有些透传DTU连接服务器会发送个什么字符串信息(比如welcome。。啥的,转成字节就是乱码),是不适合我dtu下面的设备所使用的modbus协议的,最好通过配置让dtu别发这个。 至于断开socket连接,我用的supersocket,最近在用workerman,框架里都有写好的方法,比如session.Close();,所以多看文档。
Dereky 2017-10-10
  • 打赏
  • 举报
回复
遇到同样的问题,请教下 FixedHeaderReceiveFilter 中,发现协议不符合,怎么关闭连接啊??
bianchao1 2017-03-25
  • 打赏
  • 举报
回复
引用 3 楼 xuggzu 的回复:
主要是你的头格式不合理。如果头能改的话,加一个多字节(长点好,比如四字节)的标志,接收根据此标志判读数据。
头部可以附加固定内容,最多16个字节,是DTU的注册ID,会附加在每条转发信息的前面,用来区分多个DTU设备,从而得知是哪个设备转发的信息。 可惜不提供结尾加字节的功能,不然就可以用“带起止符的协议”了。 我再研究研究,加一个 ff 00 ff 02的头,给02号DTU设备,ff 00 ff 为起始符,然后试试套用supersocket的结束符协议,或者固定数量分隔符协议。 非常感谢!
xuzuning 2017-03-25
  • 打赏
  • 举报
回复
你的 自定义协议格式 不正确! 缺少表示帧开始的标识 RS232通讯协议 格式:EBH,地址,命令,数据长度,数据1,...数据n,冗余 其中 EBH:为帧起始位
bianchao1 2017-03-25
  • 打赏
  • 举报
回复
引用 2 楼 sp1234 的回复:
如果使用比较容易区分消息结束的协议,例如使用“换行回车”做为文本消息的结束标志,或者使用某个32字节的“长标志”作为消息结束标志,那么你可以一直处理消息,不用关闭。 但是假设你的信令是这种,一旦错位了就矫正不了,那么还是只好关闭连接。
哦,我用的DTU透传,协议是买的设备定的我改不了,看来只能关闭连接了,非常感谢!
xuggzu 2017-03-25
  • 打赏
  • 举报
回复
主要是你的头格式不合理。如果头能改的话,加一个多字节(长点好,比如四字节)的标志,接收根据此标志判读数据。
  • 打赏
  • 举报
回复
如果使用比较容易区分消息结束的协议,例如使用“换行回车”做为文本消息的结束标志,或者使用某个32字节的“长标志”作为消息结束标志,那么你可以一直处理消息,不用关闭。 但是假设你的信令是这种,一旦错位了就矫正不了,那么还是只好关闭连接。
  • 打赏
  • 举报
回复
连续出了5次错误,就立刻关闭连接吧。
bianchao1 2017-03-25
  • 打赏
  • 举报
回复
感谢各位大牛的回复, 我要实现的功能是服务器(中心)接收由DTU设备转发的485总线上的数据。服务器使用supersocket框架。 总线上的设备发送的数据遵循modbus rtu协议,格式为 地址(1)命令(1)数据长度(1)数据内容(n)CRC(2),例如0c 03 02 00 00 95 85 DTU设备转发时,提供在每条数据最前面附加1-16个字节的功能,内容固定,作为区别DTU或分站的ID。 所以可以用supersocket自定义协议解析,可以这样 附加三个字节 23 23 05 前两个字节为#符号第三个字节为ID 23 23 05 0c 03 02 00 00 95 85 起始符(2) DTU设备ID(1) 地址(1)命令(1)数据长度(1)数据内容(n)CRC(2) 起始符为两个#符号,23H是其ascii码。 这样靠##来作为起始符,分割每一条信息。 如果遇到错误信息比如 AA,会变成23 23 05 AA,下一帧也是23 23 开头, 自定义一个起始符加数据长度的解析方式,不知是否可行,先试验一下。

111,093

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧