使用SuperSocket打造逾10万长连接的Socket服务

简介: 原文:使用SuperSocket打造逾10万长连接的Socket服务SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。你无须了解如何使用 Socket, 如何维护 Socket 连接和 Socket 如何工作,但是你却可以使用 SuperSocket 很容易的开发出一款 Socket 服务器端软件,例如游戏服务器,GPS 服务器, 工业控制服务和数据采集服务器等等。
原文: 使用SuperSocket打造逾10万长连接的Socket服务

SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。你无须了解如何使用 Socket, 如何维护 Socket 连接和 Socket 如何工作,但是你却可以使用 SuperSocket 很容易的开发出一款 Socket 服务器端软件,例如游戏服务器,GPS 服务器, 工业控制服务和数据采集服务器等等。

 

PS:上面这句话复制官网的,好了,总之告诉大家SuperSocket已经很强大、很稳定、方便。

如果你没有Socket基础,首先要了解协议;本人两年都在做智能穿戴,接触大量硬件厂商,发现大部分是“带起止符的协议”(BeginEndMarkReceiveFilter)或者“头部格式固定并且包含内容长度的协议”(FixedHeaderReceiveFilter)

 

我简单介绍这两种协议。

代码上我就二选一介绍FixedHeaderReceiveFilter吧

首先新建项目,在Nuget管理器上搜索“SuperSocket”安装“SuperSocket”和“SuperSocket.Engine”

 

 

接下来你需要实现

1、 IRequestInfo(请求信息,一次数据包)

    public class MyRequestInfo : IRequestInfo
    {
        public MyRequestInfo(byte[] header, byte[] bodyBuffer)
        {
            Key = ((header[0] * 256) + header[1]).ToString();
            Data = bodyBuffer;
        }
        /// <summary>
        /// 协议号对应自定义命令Name,会触摸自定义命令
        /// </summary>
        public string Key { get; set; }
        /// <summary>
        /// 正文字节码
        /// </summary>
        public byte[] Data { get; set; }
        /// <summary>
        /// 正文文本,大部分协议都不是这么玩的
        /// </summary>
        public string Body
        {
            get
            {
                return Encoding.UTF8.GetString(Data);
            }
        }
    }

2、 FixedHeaderReceiveFilter<IRequestInfo>(数据包的解析)

    public class MyReceiveFilter : FixedHeaderReceiveFilter<MyRequestInfo>
    {
        /// +-------+---+-------------------------------+
        /// |request| l |                               |
        /// | name  | e |    request body               |
        /// |  (2)  | n |                               |
        /// |       |(2)|                               |
        /// +-------+---+-------------------------------+
        public MyReceiveFilter()
        : base(4)
        {

        }

        protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
        {
            return (int)header[offset + 2] * 256 + (int)header[offset + 3];
        }
        
        protected override MyRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
        {
            var body = bodyBuffer.Skip(offset).Take(length).ToArray();
            return new MyRequestInfo(header.Array, body);
        }
    }

3、 AppSession<TAppSession, TRequestInfo>(Session会话,服务端管理客户端的连接、信息)

    public class MySession : AppSession<MySession, MyRequestInfo>
    {
        public MySession()
        {

        }protected override void OnSessionStarted()
        {

        }

        protected override void OnInit()
        {
            base.OnInit();
        }

        protected override void HandleUnknownRequest(MyRequestInfo requestInfo)
        {

        }
        
        protected override void HandleException(Exception e)
        {

        }

        protected override void OnSessionClosed(CloseReason reason)
        {
            base.OnSessionClosed(reason);
        }
    }

4、 AppServer<TAppSession, TRequestInfo>(监听服务)

    public class MyServer : AppServer<MySession, MyRequestInfo>
    {
        /// <summary>
        /// 通过配置文件安装服务从这里启动
        /// </summary>
        public MyServer()
            : base(new DefaultReceiveFilterFactory<MyReceiveFilter, MyRequestInfo>())
        {
            this.NewSessionConnected += MyServer_NewSessionConnected;
            this.SessionClosed += MyServer_SessionClosed;
        }
        /// <summary>
        /// winform启动,不使用这里的事件
        /// </summary>
        public MyServer(SessionHandler<MySession> NewSessionConnected, SessionHandler<MySession, CloseReason> SessionClosed)
            : base(new DefaultReceiveFilterFactory<MyReceiveFilter, MyRequestInfo>())
        {
            this.NewSessionConnected += NewSessionConnected;
            this.SessionClosed += SessionClosed;
        }

        protected override void OnStarted()
        {
            //启动成功
            LogHelper.WriteLog(string.Format("Socket启动成功:{0}:{1}", this.Config.Ip, this.Config.Port));
        }

        void MyServer_NewSessionConnected(MySession session)
        {
            //连接成功
        }

        void MyServer_SessionClosed(MySession session, CloseReason value)
        {

        }
    }

好了,你可以跑起来

            var config = new SuperSocket.SocketBase.Config.ServerConfig()
            {
                Name = "SSServer",
                ServerTypeName = "SServer",
                ClearIdleSession= true, //60秒执行一次清理90秒没数据传送的连接
                ClearIdleSessionInterval = 60,
                IdleSessionTimeOut = 90,
                MaxRequestLength = 2048, //最大包长度
                Ip = "Any",
                Port = 18888,
                MaxConnectionNumber = 100000,
            };
            app = new MyServer(app_NewSessionConnected, app_SessionClosed);
            LogHelper.SetOnLog(new LogHelper.LogEvent((m) =>
            {
                txtAll.Text = string.Join(" ", m, "\r\n");
                txtAll.Select(txtAll.TextLength, 0);
                txtAll.ScrollToCaret();
            }));
            app.Setup(config);
            if (!app.Start())
            {
                LogHelper.WriteLog(string.Format("Socket {0}:{1}启动失败,请检查权限或端口是否被占用!", config.Ip, config.Port));
            }

 

有关性能,本人在本地(笔记本i5 4200H/12G)测试10万连接同时在线,没有问题;当然啦,跟业务有关,比如你要做的是IM群聊,数据到服务端需要频繁计算。

 

最后我还有一些提示

1、 尽可能使用安装成windows服务,这样性能、稳定性更好;如果前期连接数不大也可用winform中启动

2、 如果终端可选.net,那使用SuperSocket.ClientEngine比自己又重新敲更稳健

3、 心跳包应在终端向服务端发送,服务端响应即可,切勿在服务端向终端发心跳(服务端拥有超时机制,若超时终端重连即可)

4、 如果需要支持WebSocket,参考SuperWebScoket;有的网友直接在WebSocket直接连接SuperSocket立即断开

5、 有的人问怎么传JSON,参照本文图1先把数据转JSON再转byte[]放到正文内容部分

6、 做Socket开发,可能经常需要在十进制、十六进制、256进制之间转换(尤其是在省流量的仅支持2G网络的硬件)

7、如果你在做硬件开发,你所处的电脑又没有外网IP,可使用ngrok反向代理TCP,让别人代理访问你的电脑

 

有关其他协议或介绍请参考官方文档

http://docs.supersocket.net/v1-6/zh-CN

 

有关客户端或本例子代码(可直接运行),如果你想先运行看看结果,再自己敲一遍

链接: https://pan.baidu.com/s/1hs09vb2 密码: 4ntg

 

目录
相关文章
|
11月前
|
网络安全
socket服务部署到服务端后启动失败Cannot assign requested address: bind 的总结
socket服务部署到服务端后启动失败Cannot assign requested address: bind 的总结
199 0
|
11月前
|
缓存 网络协议 算法
【Python基础篇021】黏包现象丨udp的socket服务
【Python基础篇021】黏包现象丨udp的socket服务
10930 0
|
前端开发 中间件
挑战21天手写前端框架 day7 使用 Socket 实现 esbuild 的热加载服务 hmr
挑战21天手写前端框架 day7 使用 Socket 实现 esbuild 的热加载服务 hmr
412 0
挑战21天手写前端框架 day7 使用 Socket 实现 esbuild 的热加载服务 hmr
|
编解码 Python
Python 技术篇 - 修改源码解决中文主机名导致的flask、socket服务起不来问题: ‘utf-8‘ codec can‘t decode byte 0xc0 in position...
Python 技术篇 - 修改源码解决中文主机名导致的flask、socket服务起不来问题: ‘utf-8‘ codec can‘t decode byte 0xc0 in position...
304 0
Python 技术篇 - 修改源码解决中文主机名导致的flask、socket服务起不来问题: ‘utf-8‘ codec can‘t decode byte 0xc0 in position...
|
存储 负载均衡 算法
RPC原来就是Socket——RPC框架到dubbo的服务动态注册,服务路由,负载均衡演化
  序:RPC就是使用socket告诉服务端我要调你的哪一个类的哪一个方法然后获得处理的结果。服务注册和路由就是借助第三方存储介质存储服务信息让服务消费者调用。然我们自己动手从0开始写一个rpc功能以及实现服务注册,动态上下线,服务路由,负载均衡。
1535 0
|
PHP 网络协议
使用PHP创建SOCKET服务
最近好奇,接触接触PHP的socket编程,发现还是很好玩儿的。下面贴出来demo源码跟大家分享 Server.php &lt;?php /** * PHP Socket Server * */ //确保客户端连接时不会超时 error_reporting(0);
2707 0
|
网络协议 Go 移动开发
golang(4):编写socket服务,简单支持命令
本文的原文连接是: http://blog.csdn.net/freewebsys/article/details/46881213 转载请必须注明出处! 1,socket服务 使用golang开发socket服务还是非常简单的。 socket的库都封装好了。 参考文档: https://github.com/astaxie/build-web-application
990 0