一、SuperSocket 1.6 自定义帧过滤的官方文档地址
http://docs.supersocket.net/v1-6/zh-CN/Implement-Your-Own-Communication-Protocol-with-IRequestInfo,-IReceiveFilter-and-etc
二、此博客的内容
博文描述如何根据官方的模板,实现自己的基于MODBUS的通信帧的过滤。
三、接口及其作用
没有内容
四、实现方法(包含注释)
1.继承FixedHeaderReceiveFilter类
public class MyReceiveFilter : FixedHeaderReceiveFilter
{
public MyReceiveFilter()
:base(3)
{ }
2.实现2个重载函数
protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
{
if (header.Length >= offset + 2)
{
//length为头部(包含1字节的Length长度)
byte data = header[offset+2];
int len = (int)data+2;
//int len = (int)data;
return len;
}
else
return -1;
}
protected override MyRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
{
byte[] buffer = new byte[header.Array.Length + length];
Buffer.BlockCopy(header.Array, 0, buffer, 0, header.Array.Length);
Buffer.BlockCopy(bodyBuffer, offset, buffer, header.Array.Length, length);
if (true == CRC16.VerifyFrame(buffer, 0, (uint)buffer.Length))
{
MyRequestInfo res = new MyRequestInfo();
string entireFrame = BytesToHEXStr(header.Array) + BytesToHEXStr(bodyBuffer.CloneRange(offset, length));
res.DeviceLogicalCode = entireFrame.Substring(0, 2);
res.Seq = entireFrame.Substring(2, 2);
res.Length = entireFrame.Substring(4, 2);
int dataLen = int.Parse(HEXtoDEC(res.Length));
res.Data = entireFrame.Substring(6, dataLen * 2);
res.Crc = entireFrame.Substring(6 + dataLen * 2, 4);
/*将数据更新到链表*/
List<int> lst_data = new List<int>();
dataLen /= 2;
for (int i = 0; i < dataLen; i++)
{
int val = ((int)bodyBuffer[offset + 2*i]) * 256 + bodyBuffer[offset + 2*i + 1];
if (val > 0x8000)/*是个负数*/
val = (0x8000-(val - 0x8000) + 1);
lst_data.Add(val);
}
res.DataArray = lst_data;
return res;/*解析出1个完整的帧,则返回实例*/
}
else { return null;/*此缓冲区中并不包含一个完整的帧,则返回 NULL*/ }
}
五、具体的程序运行流程解析
1.自定义帧过滤的上层处理函数的关键代码在 AppSession.cs文件中。
2.当接收到帧之后,程序的调用流程是
IAppSession.ProcessRequest()->FilterRequest()->自定义的帧过滤函数
3.IAppSession.ProcessRequest()部分代码及注释
int IAppSession.ProcessRequest(byte[] readBuffer, int offset, int length, bool toBeCopied)
{
int rest, offsetDelta;
while (true)
{/*这里有一个死循环,正常情况下,在rest<=0的时候会跳出,所以,在Filter()中,只要数据流处理完了,不管是否有效,都要让rest为0*/
var requestInfo = FilterRequest(readBuffer, offset, length, toBeCopied, out rest, out offsetDelta);
if (requestInfo != null)
{
try
{
AppServer.ExecuteCommand(this, requestInfo);
}
catch (Exception e)
{
HandleException(e);
}
}
if (rest <= 0)
{
return offsetDelta;
}
//Still have data has not been processed
offset = offset + length - rest;
length = rest;
}
}
#endregion
}
TRequestInfo FilterRequest(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest, out int offsetDelta)
{
if (!AppServer.OnRawDataReceived(this, readBuffer, offset, length))/*没进去看是干嘛的*/
{
rest = 0;
offsetDelta = 0;
return null;
}
var currentRequestLength = m_ReceiveFilter.LeftBufferSize;/*当前请求的长度,即未处理的数据流长度*/
var requestInfo = m_ReceiveFilter.Filter(readBuffer, offset, length, toBeCopied, out rest);
if (m_ReceiveFilter.State == FilterState.Error)
{/*如果你在自定义的解析函数里面,返回了帧错误,则会进入这里,关闭连接,并返回*/
rest = 0;
offsetDelta = 0;
Close(CloseReason.ProtocolError);
return null;
}
var offsetAdapter = m_ReceiveFilter as IOffsetAdapter;
offsetDelta = offsetAdapter != null ? offsetAdapter.OffsetDelta : 0;
if (requestInfo == null)
{/*如果未解析出一个完整的帧,则让 请求长度 等于 剩余长度(LeftBufferSize与rest的区别在前面提过)*/
//current buffered length
currentRequestLength = m_ReceiveFilter.LeftBufferSize;
}
else
{/*更新 当前请求长度*/
//current request length
currentRequestLength = currentRequestLength + length - rest;
}
if (currentRequestLength >= AppServer.Config.MaxRequestLength)
{
if (Logger.IsErrorEnabled)
Logger.Error(this, string.Format("Max request length: {0}, current processed length: {1}", AppServer.Config.MaxRequestLength, currentRequestLength));
Close(CloseReason.ProtocolError);
return null;
}
//If next Receive filter wasn't set, still use current Receive filter in next round received data processing
if (m_ReceiveFilter.NextReceiveFilter != null)
m_ReceiveFilter = m_ReceiveFilter.NextReceiveFilter;
return requestInfo;
}