SuperSocket 提供了几种通用的协议解析工具,比如上一篇文章用到的结束符过滤器:TerminatorReceiveFilter (TerminatorReceiveFilterFactory)。
通常,如果传输数据而不是简单的命令,使用FixedHeaderReceiveFilter(头部格式固定并且包含内容长度的协议过滤器)会更加普遍。
1.定义帧格式
通常,定义帧的格式,大都是类似下面这种格式
按照FixedHeaderReceiveFilter的规则,则可将此帧格式解析为
根据此帧格式自定义一个请求消息
public struct constNum
{
public const int Headerlen = 5; //头长度
public const int lenloc = 5; //指示长度所在位置
}
/*
public struct frameHeaderFormat
{
public byte start ; //前导码
public byte dstaddr; //目的地址
public byte srcaddr; //源地址
public byte func; //功能码
public byte datalen ; //数据长度(字节)
}
*/
public class MyRequestInfo : IRequestInfo
{
public string Key { get; set; }
public byte start; //前导码
public byte dstaddr; //目的地址
public byte srcaddr; //源地址
public byte func; //功能码
public byte datalen; //数据长度(字节)
public List<byte> data = new List<byte>(); //数据内容
public MyRequestInfo(byte[] header, byte[] bodyBuffer, int length)
{
start = header[0];
dstaddr = header[1];
srcaddr = header[2];
func = header[3 ];
datalen = (byte)length ;
data = bodyBuffer.ToList();
}
2.过滤器
使用:头部格式固定并且包含内容长度的协议过滤器
public class MyReceiveFilter : FixedHeaderReceiveFilter<MyRequestInfo>
{
public MyReceiveFilter()
: base(constNum.Headerlen)
{
}
protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
{
return (int)header[offset + length - 1];
}
protected override MyRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
{
byte[] bb = new byte[length] ;
Array.Copy(bodyBuffer, offset, bb, 0, length);
return new MyRequestInfo(header.Array, bb, length);
}
}
3.其他代码
会话控制类,服务器类
public class MYSession : AppSession<MYSession, MyRequestInfo>
{
public int ID { get; set; }
/// <summary>
/// 用户连接会话
/// </summary>
protected override void OnSessionStarted()
{
Console.WriteLine("New Request");
base.OnSessionStarted();
}
/// <summary>
/// 未知的用户请求命令
/// </summary>
/// <param name="requestInfo"></param>
protected override void HandleUnknownRequest(MyRequestInfo requestInfo)
{
base.HandleUnknownRequest(requestInfo);
}
protected override void OnSessionClosed(CloseReason reason)
{
Console.WriteLine("会话关闭");
base.OnSessionClosed(reason);
}
}
public class MYServer : AppServer<MYSession, MyRequestInfo>
{
public MYServer()
: base(new DefaultReceiveFilterFactory<MyReceiveFilter, MyRequestInfo>())
{
}
}
在程序合适的地方添加
public MYServer appServer = new MYServer();
appServer.NewRequestReceived += appServer_NewRequestReceived ;
if (!appServer.Setup(60000)) //开启的监听端口
{
Console.WriteLine("Failed to Setup!");
return;
}
if (!appServer.Start())
{
Console.WriteLine("Failed to Start!");
return;
}
Console.WriteLine("The server started successfully.");
实现接收请求函数
int cnt=0;
void appServer_NewRequestReceived(MYSession session, MyRequestInfo requestInfo)
{
var info = requestInfo;
this.BeginInvoke
(
(ThreadStart)delegate()
{ //显示数据
textBox_recv.Text = "dstaddr:" + info.dstaddr.ToString();
textBox_recv.Text += "\r\nsrcaddr:" + info.srcaddr.ToString();
textBox_recv.Text += "\r\n:" + System.Text.Encoding.Default.GetString(info.data.ToArray()) + "\r\n";
label_cnt.Text = (++cnt).ToString();
}
);
}
4.测试
使用Socket 调试工具,创建Client,端口60000
发送数据进行测试
为了方便,我使用文本发送方式。前5个空格加后面32个字符。空格的ascii码值为32,所以如上面所示。
假如发送端的数据出现丢失,错误会一直累积下去,除非重新启动服务。所以这种程序并不稳健。实际项目中不建议使用这种方法。