SuperSocket实战---使用SuperSocket的FixedHeaderReceiveFilter进行通信

       

目录

一、定义消息格式

二、建立一个TCP客户端

三、SuperSocket作为服务端

1、新建控制台程序SuperSocketServer        ​

 2、使用nuget工具安装SuperSocket和SuperSocket.Engine库

3、实现3个类: Filter、Session和Server

4、Main函数关键代码:


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

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

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

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

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

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

一、定义消息格式

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

Public class SuperSocketMessage
    {
        public byte[] Start;//4个字节
        public ushort Type;//2个字节, 1表示文字消息,2表示图片消息,其他表示未知,不能解析
        public int Len;//4个字节
        public string Message;//文本信息
        public byte[] Tail;//消息结尾

        public SuperSocketMessage()
        {
            Start = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
            Tail = new byte[] { 0x1F, 0x1F, 0x1F, 0x1F };
        }

        public byte[] ToBytes()
        {
            List<byte> list = new List<byte>();
            list.AddRange(Start);
            var t = BitConverter.GetBytes(Type);
            list.AddRange(t);

            var t3 = System.Text.Encoding.UTF8.GetBytes(Message);
            var t2 = BitConverter.GetBytes(t3.Length);//注意,这里不是Message.Length,而是Message转化成字节数组后的Lenght

            list.AddRange(t2);
            list.AddRange(t3);

            return list.ToArray();
        }
}

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

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

二、建立一个TCP客户端

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

        2、添加Class: MyTcpClient.cs

class MyTcpClient
    {
        private System.Net.Sockets.TcpClient tcpClient;
        public MyTcpClient(string ip, int port)
        {

            tcpClient = new System.Net.Sockets.TcpClient(ip, port);
            byte[] recData = new byte[1024];
            Action a = new Action(() =>
            {
                while (true)
                {
                    tcpClient.Client.Receive(recData);
                    var msg = System.Text.Encoding.UTF8.GetString(recData);
                    Console.WriteLine(msg);
                }
            });
            a.BeginInvoke(null, null);

        }

        public void Send(string message)
        {
            var data = System.Text.Encoding.UTF8.GetBytes(message);
            tcpClient.Client.Send(data);
        }
        public void Send(byte[] message)
        {
            tcpClient.Client.Send(message);
        }
}

        3、Main函数:

static void Main(string[] args)
        {
            MyTcpClient c = new MyTcpClient("127.0.0.1", 2020);
            SuperSocketMessage.SSMessage msg = new SuperSocketMessage.SSMessage();
            while (true)
            {
                string m = Console.ReadLine();
                msg.Type = 1;
                msg.Message = m;
                c.Send(msg.ToBytes());
            }
        }

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

        其实客户端最好用TCP/UDP测试工具软件SocketTool.exe模拟客户端。

                                                        

三、SuperSocket作为服务端

1、新建控制台程序SuperSocketServer       

 

 2、使用nuget工具安装SuperSocket和SuperSocket.Engine库

3、实现3个类: Filter、Session和Server

Filter类如下:

public class MyReceiveFilter : FixedHeaderReceiveFilter<BinaryRequestInfo>
    {

        public MyReceiveFilter()
            : base(10)//消息头部长度
        {        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="header">*byte[] header * 缓存的数据,这个并不是单纯只包含协议头的数据,有时候tcp协议长度为409600,很多</param>
        /// <param name="offset">头部数据从 缓存的数据 中开始的索引,一般为0.(tcp协议有可能从405504之类的一个很大数据开始)</param>
        /// <param name="length">这个length和base(10)中的参数相等</param>
        /// <returns></returns>
        protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
        {
            return GetBodyLengthFromHeader(header, offset, length, 6, 4);//6表示第几个字节开始表示长度.4:由于是int来表示长度,int占用4个字节
        }
        protected override BinaryRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
        {

            byte[] body = new byte[length];
            Array.Copy(bodyBuffer, offset, body, 0, length);

            Int16 type = BitConverter.ToInt16(header.ToArray(), 4);

            BinaryRequestInfo r = new BinaryRequestInfo(type.ToString(), body);
            return r;

            //以下的代码,不解析body,全部返给上一层
            //byte[] h = header.ToArray();
            //byte[] full = new byte[h.Count()+length];
            //Array.Copy(h, full, h.Count());
            //Array.Copy(body, 0, full, h.Count(), body.Length );
            //BinaryRequestInfo r = new BinaryRequestInfo("",full);
            //return r;

        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="header">需要解析的数据</param>
        /// <param name="offset">头部数据从header中开始的索引,一般为0,也可能不是0</param>
        /// <param name="length">这个length和base(10)中的参数相等</param>
        /// <param name="lenStartIndex">表示长度的字节从第几个开始</param>
        /// <param name="lenBytesCount">几个字节来表示长度:4个字节=int,2个字节=int16,1个字节=byte</param>
        /// <returns></returns>
        private int GetBodyLengthFromHeader(byte[] header, int offset, int length, int lenStartIndex, int lenBytesCount)
        {
            var headerData = new byte[lenBytesCount];
            Array.Copy(header, offset + lenStartIndex, headerData, 0, lenBytesCount);//
            if (lenBytesCount == 1)
            {
                int i = headerData[0];
                return i;
            }
            else if (lenBytesCount == 2)
            {
                int i = BitConverter.ToInt16(headerData, 0);
                return i;
            }
            else //  if (lenBytesCount == 4)
            {
                int i = BitConverter.ToInt32(headerData, 0);
                return i;
            }
        }
}

Server和Session类如下:

public class MyServer : AppServer<MySession, BinaryRequestInfo>
    {
        public MyServer()
            : base(new DefaultReceiveFilterFactory<MyReceiveFilter, BinaryRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory)
        {
        }
    }

    public class MySession : AppSession<MySession, BinaryRequestInfo>
    {
    }

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

4、Main函数关键代码:

配置:

MyServer appServer = new MyServer();
            var se = new SuperSocket.SocketBase.Config.ServerConfig();
            se.TextEncoding = "Unicode";// System.Text.Encoding.
            se.TextEncoding = "gbk";// System.Text.Encoding.
            se.Ip = "127.0.0.1";
            se.Port = 2020;
            se.Mode = SocketMode.Tcp;

注册,启动:

if (!appServer.Setup(se)) //Setup with listening port
            {
                Console.WriteLine("Failed to setup!");
                Console.ReadKey();
                return;
            }
            Console.WriteLine();
            //Try to start the appServer
            if (!appServer.Start())
            {
                Console.WriteLine("Failed to start!");
                Console.ReadKey();
                return;
            }

注册事件:

appServer.NewSessionConnected += appServer_NewSessionConnected;
appServer.SessionClosed += appServer_SessionClosed;
appServer.NewRequestReceived += XXXXXXXXXXXXXXXXXXX

解析方法:

static void appServer_NewRequestReceived(MySession session, BinaryRequestInfo requestInfo)
        {
            string key = requestInfo.Key;
            switch (key)
            {
                case "1":
                    Console.WriteLine("Get message from " + session.RemoteEndPoint.ToString() + ":" + System.Text.Encoding.UTF8.GetString(requestInfo.Body));
                    break;
                case "2":
                    Console.WriteLine("Get image");
                    break;
                default:
                    Console.WriteLine("Get unknown message.");
                    break;
            }
        }

好,完成.截图如下:

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

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

使用SuperSocket的FixedHeaderReceiveFilter进行通信 - 瘦馬 - 博客园 (cnblogs.com)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值