1. 程式人生 > >使用SuperSocket打造逾10萬長連接的Socket服務

使用SuperSocket打造逾10萬長連接的Socket服務

toa toarray 都在 tor winform over mar 機制 技術

原文:使用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

使用SuperSocket打造逾10萬長連接的Socket服務