使用SuperSocket的FixedHeaderReceiveFilter进行通信

释放双眼,带上耳机,听听看~!

前几个月有个项目需要和其他设备通信,需要用到TCP和UDP通信.本来开始也是用的C#原生态socket通信库,但是后来发现了一个”我不想说他名字坑爹库”,经过测试,用起来还挺顺手,就直接把这个”我不想说他名字坑爹库”引入了项目中.还把使用方法写在了博客园,测试demo还上传了代码(通讯库并没有源码,只有dll文件).

结果,大约三个月后,有人给我留言,说他下载了”我不想说他名字坑爹库”的demo源码,并且引入了项目中,但是要收费,已经过期,他们的项目已经挂了.开始我还不信((当时我在出差,其实我自己项目中的这个坑爹库也过期了),一直以为”我不想说他名字坑爹库”是免费的,是他们项目中的其他库出问题了,后来发现,这个库确实要收费的,并且还价格不菲.我简直*****了.

幸好我们的项目还没上线,不然真的要死翘翘了,丢人要丢到外国去了.不过下载我源码的兄弟就真的对不起了.我在这里真诚的为你们感到sorry.

后来嘛,就只能换噻,好在代码框架还行,通信和业务耦合度很低,换通信框架很容易.在这个开源大行其道的世界,遇到收费的库,我也是无语.再后来,就找到SuperSocket了,开源的,老板再也不担心软件过期了.

这个文章就不介绍SuperSocket的其他东西了,只写FixedHeaderReceiveFilter通信例子.

 

需求:一个tcp服务端,多个tcp客户端,客户端可向服务端发送消息,服务端向连接的每个客户端广播消息.

 

1.定义消息格式

要发送的消息格式直接给出来:

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

 1 Public class SuperSocketMessage
 2     {
 3         public byte[] Start;//4个字节
 4         public ushort Type;//2个字节, 1表示文字消息,2表示图片消息,其他表示未知,不能解析
 5         public int Len;//4个字节
 6         public string Message;//文本信息
 7         public byte[] Tail;//消息结尾
 8 
 9         public SuperSocketMessage()
10         {
11             Start = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
12             Tail = new byte[] { 0x1F, 0x1F, 0x1F, 0x1F };
13         }
14 
15         public byte[] ToBytes()
16         {
17             List<byte> list = new List<byte>();
18             list.AddRange(Start);
19             var t = BitConverter.GetBytes(Type);
20             list.AddRange(t);
21 
22             var t3 = System.Text.Encoding.UTF8.GetBytes(Message);
23             var t2 = BitConverter.GetBytes(t3.Length);//注意,这里不是Message.Length,而是Message转化成字节数组后的Lenght
24 
25             list.AddRange(t2);
26             list.AddRange(t3);
27 
28             return list.ToArray();
29         }
30 }

View Code

请记住以下几个数字或信息:

  1. 消息头部包含三个元素:开始,类型和消息body的长度
  2. 从第六个字节开始表示长度
  3. 表示长度字节数是4个字节
  4. Body的长度不要搞混,不是Message的字符串长度,而是Message转换字节数组后的长度,调试的时候,开始用了Message的字符串长度,给我造成了几个小时的浪费.
  5. 以上信息请和下面的MyReceiveFilter类中的参数对照看.

2.建立一个tcp客户端

1.新建一个控制台程序TcpClientTest,这里为了测试的客观性,直接使用C#的TcpClient作为客户端.

2.添加Class: MyTcpClient.cs

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

 1 class MyTcpClient
 2     {
 3         private System.Net.Sockets.TcpClient tcpClient;
 4         public MyTcpClient(string ip, int port)
 5         {
 6 
 7             tcpClient = new System.Net.Sockets.TcpClient(ip, port);
 8             byte[] recData = new byte[1024];
 9             Action a = new Action(() =>
10             {
11                 while (true)
12                 {
13                     tcpClient.Client.Receive(recData);
14                     var msg = System.Text.Encoding.UTF8.GetString(recData);
15                     Console.WriteLine(msg);
16                 }
17             });
18             a.BeginInvoke(null, null);
19 
20         }
21 
22         public void Send(string message)
23         {
24             var data = System.Text.Encoding.UTF8.GetBytes(message);
25             tcpClient.Client.Send(data);
26         }
27         public void Send(byte[] message)
28         {
29             tcpClient.Client.Send(message);
30         }
31 }

View Code

3.Main函数:

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

 1 static void Main(string[] args)
 2         {
 3             MyTcpClient c = new MyTcpClient(\"127.0.0.1\", 2020);
 4             SuperSocketMessage.SSMessage msg = new SuperSocketMessage.SSMessage();
 5             while (true)
 6             {
 7                 string m = Console.ReadLine();
 8                 msg.Type = 1;
 9                 msg.Message = m;
10                 c.Send(msg.ToBytes());
11             }
12         }

View Code

客户端实现控制台输入任意字符串后,封装成消息报文,发给服务端;同时,开启一个线程,接收服务端的消息.这里客服端就没有按照标准来解析了.直接定义缓冲区1024.

3.SuperSocket作为服务端

  1. 新建控制台程序SuperSocketServer
  2. 使用nuget工具安装SuperSocket和SuperSocket.Engine库
  3. 实现3个类: Filter,Session和Server

Filter类如下:

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

 1  public class MyReceiveFilter : FixedHeaderReceiveFilter<BinaryRequestInfo>
 2     {
 3 
 4         public MyReceiveFilter()
 5             : base(10)//消息头部长度
 6         {        }
 7         /// <summary>
 8         /// 
 9         /// </summary>
10         /// <param name=\"header\">*byte[] header * 缓存的数据,这个并不是单纯只包含协议头的数据,有时候tcp协议长度为409600,很多</param>
11         /// <param name=\"offset\">头部数据从 缓存的数据 中开始的索引,一般为0.(tcp协议有可能从405504之类的一个很大数据开始)</param>
12         /// <param name=\"length\">这个length和base(10)中的参数相等</param>
13         /// <returns></returns>
14         protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
15         {
16             return GetBodyLengthFromHeader(header, offset, length, 6, 4);//6表示第几个字节开始表示长度.4:由于是int来表示长度,int占用4个字节
17         }
18         protected override BinaryRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
19         {
20 
21             byte[] body = new byte[length];
22             Array.Copy(bodyBuffer, offset, body, 0, length);
23 
24             Int16 type = BitConverter.ToInt16(header.ToArray(), 4);
25 
26             BinaryRequestInfo r = new BinaryRequestInfo(type.ToString(), body);
27             return r;
28 
29             //以下的代码,不解析body,全部返给上一层
30             //byte[] h = header.ToArray();
31             //byte[] full = new byte[h.Count()+length];
32             //Array.Copy(h, full, h.Count());
33             //Array.Copy(body, 0, full, h.Count(), body.Length );
34             //BinaryRequestInfo r = new BinaryRequestInfo(\"\",full);
35             //return r;
36 
37         }
38 
39         /// <summary>
40         /// 
41         /// </summary>
42         /// <param name=\"header\">需要解析的数据</param>
43         /// <param name=\"offset\">头部数据从header中开始的索引,一般为0,也可能不是0</param>
44         /// <param name=\"length\">这个length和base(10)中的参数相等</param>
45         /// <param name=\"lenStartIndex\">表示长度的字节从第几个开始</param>
46         /// <param name=\"lenBytesCount\">几个字节来表示长度:4个字节=int,2个字节=int16,1个字节=byte</param>
47         /// <returns></returns>
48         private int GetBodyLengthFromHeader(byte[] header, int offset, int length, int lenStartIndex, int lenBytesCount)
49         {
50             var headerData = new byte[lenBytesCount];
51             Array.Copy(header, offset + lenStartIndex, headerData, 0, lenBytesCount);//
52             if (lenBytesCount == 1)
53             {
54                 int i = headerData[0];
55                 return i;
56             }
57             else if (lenBytesCount == 2)
58             {
59                 int i = BitConverter.ToInt16(headerData, 0);
60                 return i;
61             }
62             else //  if (lenBytesCount == 4)
63             {
64                 int i = BitConverter.ToInt32(headerData, 0);
65                 return i;
66             }
67         }
68 }

View Code

Server和Session类如下:

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

 1 public class MyServer : AppServer<MySession, BinaryRequestInfo>
 2     {
 3         public MyServer()
 4             : base(new DefaultReceiveFilterFactory<MyReceiveFilter, BinaryRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory)
 5         {
 6         }
 7     }
 8 
 9     public class MySession : AppSession<MySession, BinaryRequestInfo>
10     {
11     }

View Code

BinaryRequestInfo实现了 IRequestInfo接口,这个接口就一个key,貌似SuperSocket的所有数据都是实现这个接口

     4.Main函数关键代码:

配置:

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

1 MyServer appServer = new MyServer();
2             var se = new SuperSocket.SocketBase.Config.ServerConfig();
3             se.TextEncoding = \"Unicode\";// System.Text.Encoding.
4             se.TextEncoding = \"gbk\";// System.Text.Encoding.
5             se.Ip = \"127.0.0.1\";
6             se.Port = 2020;
7             se.Mode = SocketMode.Tcp;

View Code

注册,启动:

使用SuperSocket的FixedHeaderReceiveFilter进行通信
使用SuperSocket的FixedHeaderReceiveFilter进行通信

 1 if (!appServer.Setup(se)) //Setup with listening port
 2             {
 3                 Console.WriteLine(\"Failed to setup!\");
 4                 Console.ReadKey();
 5                 return;
 6             }
 7             Console.WriteLine();
 8             //Try to start the appServer
 9             if (!appServer.Start())
10             {
11                 Console.WriteLine(\"Failed to start!\");
12                 Console.ReadKey();
13                 return;
14             }

View Code

注册事件:

1 appServer.NewSessionConnected += appServer_NewSessionConnected;
2 appServer.SessionClosed += appServer_SessionClosed;
3 appServer.NewRequestReceived += XXXXXXXXXXXXXXXXXXX

解析方法:

 1 static void appServer_NewRequestReceived(MySession session, BinaryRequestInfo requestInfo)
 2         {
 3             string key = requestInfo.Key;
 4             switch (key)
 5             {
 6                 case \"1\":
 7                     Console.WriteLine(\"Get message from \" + session.RemoteEndPoint.ToString() + \":\" + System.Text.Encoding.UTF8.GetString(requestInfo.Body));
 8                     break;
 9                 case \"2\":
10                     Console.WriteLine(\"Get image\");
11                     break;
12                 default:
13                     Console.WriteLine(\"Get unknown message.\");
14                     break;
15             }
16         }

好.完成.截图如下:

使用SuperSocket的FixedHeaderReceiveFilter进行通信

附源码下载地址:https://download.csdn.net/download/hanghangz/11236794

项目中用nuget下载packages中的内容没有上传,文件太大了,自己去nuget吧.

给TA打赏
共{{data.count}}人
人已打赏
随笔日记

Spring Boot 高效数据聚合之道

2020-11-9 5:21:20

随笔日记

[书籍]通过《番茄工作法图解》复习番茄工作法

2020-11-9 5:21:22

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索