天天看點

簡潔實用Socket架構DotNettySocket

目錄

  • 簡介
  • 産生背景
  • 使用方式
    • TcpSocket
    • WebSocket
    • UdpSocket
  • 結尾

DotNettySocket是一個.NET跨平台Socket架構(支援.NET4.5+及.NET Standard2.0+),同時支援TcpSocket、WebSocket和UdpSocket,其基于微軟強大的DotNetty架構,力求為Socket通訊提供簡單、高效、優雅的操作方式。

安裝方式:Nuget安裝DotNettySocket即可

項目位址:https://github.com/Coldairarrow/DotNettySocket

兩年前最開始接觸物聯網的時候,需要用到Tcp及Udp通訊,為了友善使用,将原始的Socket進行了簡單的封裝,基本滿足了需求,并将架構開源。但是由于精力及實力有限,沒有進一步優化原架構。後來發現了強大的DotNetty架構,DotNetty是微軟Azure團隊開源基于Java Netty架構的移植版,其性能優異、維護團隊強大,許多.NET強大的架構都使用它。DotNetty功能強大,但是用起來還是不夠簡潔(或許是個人感覺),剛好最近項目需要用到WebSocket,是以鄙人抽時間基于DotNetty進行簡單封裝了下,撸出一個力求簡單、高效、優雅的Socket架構。

Tcp是面向連接配接的,是以服務端對連接配接的管理就至關重要,架構支援各種事件的處理、給連接配接設定連接配接名(身份辨別)、通過連接配接名找到特定連接配接、連接配接收發資料、分包、粘包處理。

  • 服務端
using Coldairarrow.DotNettySocket;
using System;
using System.Text;
using System.Threading.Tasks;

namespace TcpSocket.Server
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var theServer = await SocketBuilderFactory.GetTcpSocketServerBuilder(6001)
                .SetLengthFieldEncoder(2)
                .SetLengthFieldDecoder(ushort.MaxValue, 0, 2, 0, 2)
                .OnConnectionClose((server, connection) =>
                {
                    Console.WriteLine($"連接配接關閉,連接配接名[{connection.ConnectionName}],目前連接配接數:{server.GetConnectionCount()}");
                })
                .OnException(ex =>
                {
                    Console.WriteLine($"服務端異常:{ex.Message}");
                })
                .OnNewConnection((server, connection) =>
                {
                    connection.ConnectionName = $"名字{connection.ConnectionId}";
                    Console.WriteLine($"新的連接配接:{connection.ConnectionName},目前連接配接數:{server.GetConnectionCount()}");
                })
                .OnRecieve((server, connection, bytes) =>
                {
                    Console.WriteLine($"服務端:資料{Encoding.UTF8.GetString(bytes)}");
                    connection.Send(bytes);
                })
                .OnSend((server, connection, bytes) =>
                {
                    Console.WriteLine($"向連接配接名[{connection.ConnectionName}]發送資料:{Encoding.UTF8.GetString(bytes)}");
                })
                .OnServerStarted(server =>
                {
                    Console.WriteLine($"服務啟動");
                }).BuildAsync();

            Console.ReadLine();
        }
    }
}
           
  • 用戶端
using Coldairarrow.DotNettySocket;
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace UdpSocket.Client
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var theClient = await SocketBuilderFactory.GetUdpSocketBuilder()
                .OnClose(server =>
                {
                    Console.WriteLine($"用戶端關閉");
                })
                .OnException(ex =>
                {
                    Console.WriteLine($"用戶端異常:{ex.Message}");
                })
                .OnRecieve((server, point, bytes) =>
                {
                    Console.WriteLine($"用戶端:收到來自[{point.ToString()}]資料:{Encoding.UTF8.GetString(bytes)}");
                })
                .OnSend((server, point, bytes) =>
                {
                    Console.WriteLine($"用戶端發送資料:目标[{point.ToString()}]資料:{Encoding.UTF8.GetString(bytes)}");
                })
                .OnStarted(server =>
                {
                    Console.WriteLine($"用戶端啟動");
                }).BuildAsync();

            while (true)
            {
                await theClient.Send(Guid.NewGuid().ToString(), new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6003));
                await Task.Delay(1000);
            }
        }
    }
}

           

WebSocket與TcpSocket接口基本保持一緻,僅有的差別就是TcpSocket支援位元組的收發并且需要自行處理分包粘包。而WebSocket直接收發字元串(UTF-8)編碼,并且無需考慮分包粘包。架構目前沒有支援WSS,建議解決方案是使用Nginx轉發即可(相關資料一搜便有)

using Coldairarrow.DotNettySocket;
using System;
using System.Threading.Tasks;

namespace WebSocket.Server
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var theServer = await SocketBuilderFactory.GetWebSocketServerBuilder(6002)
                .OnConnectionClose((server, connection) =>
                {
                    Console.WriteLine($"連接配接關閉,連接配接名[{connection.ConnectionName}],目前連接配接數:{server.GetConnectionCount()}");
                })
                .OnException(ex =>
                {
                    Console.WriteLine($"服務端異常:{ex.Message}");
                })
                .OnNewConnection((server, connection) =>
                {
                    connection.ConnectionName = $"名字{connection.ConnectionId}";
                    Console.WriteLine($"新的連接配接:{connection.ConnectionName},目前連接配接數:{server.GetConnectionCount()}");
                })
                .OnRecieve((server, connection, msg) =>
                {
                    Console.WriteLine($"服務端:資料{msg}");
                    connection.Send(msg);
                })
                .OnSend((server, connection, msg) =>
                {
                    Console.WriteLine($"向連接配接名[{connection.ConnectionName}]發送資料:{msg}");
                })
                .OnServerStarted(server =>
                {
                    Console.WriteLine($"服務啟動");
                }).BuildAsync();

            Console.ReadLine();
        }
    }
}

           
  • 控制台用戶端
using Coldairarrow.DotNettySocket;
using System;
using System.Threading.Tasks;

namespace WebSocket.ConsoleClient
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var theClient = await SocketBuilderFactory.GetWebSocketClientBuilder("127.0.0.1", 6002)
                .OnClientStarted(client =>
                {
                    Console.WriteLine($"用戶端啟動");
                })
                .OnClientClose(client =>
                {
                    Console.WriteLine($"用戶端關閉");
                })
                .OnException(ex =>
                {
                    Console.WriteLine($"異常:{ex.Message}");
                })
                .OnRecieve((client, msg) =>
                {
                    Console.WriteLine($"用戶端:收到資料:{msg}");
                })
                .OnSend((client, msg) =>
                {
                    Console.WriteLine($"用戶端:發送資料:{msg}");
                })
                .BuildAsync();

            while (true)
            {
                await theClient.Send(Guid.NewGuid().ToString());

                await Task.Delay(1000);
            }
        }
    }
}
           
  • 網頁用戶端
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>菜鳥教程(runoob.com)</title>

    <script type="text/javascript">
        function WebSocketTest() {
            if ("WebSocket" in window) {
                var ws = new WebSocket("ws://127.0.0.1:6002");

                ws.onopen = function () {
                    console.log('連上服務端');
                    setInterval(function () {
                        ws.send("111111");
                    }, 1000);
                };

                ws.onmessage = function (evt) {
                    var received_msg = evt.data;
                    console.log('收到' + received_msg);
                };

                ws.onclose = function () {
                    console.log("連接配接已關閉...");
                };
            }

            else {
                alert("您的浏覽器不支援 WebSocket!");
            }
        }
    </script>

</head>
<body>
    <div id="sse">
        <a href="javascript:WebSocketTest()">運作 WebSocket</a>
    </div>
</body>
</html>
           

Udp天生便是收發一體的,以下分為服務端與用戶端僅僅是為了友善了解

using Coldairarrow.DotNettySocket;
using System;
using System.Text;
using System.Threading.Tasks;

namespace UdpSocket.Server
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var theServer = await SocketBuilderFactory.GetUdpSocketBuilder(6003)
                .OnClose(server =>
                {
                    Console.WriteLine($"服務端關閉");
                })
                .OnException(ex =>
                {
                    Console.WriteLine($"服務端異常:{ex.Message}");
                })
                .OnRecieve((server, point, bytes) =>
                {
                    Console.WriteLine($"服務端:收到來自[{point.ToString()}]資料:{Encoding.UTF8.GetString(bytes)}");
                    server.Send(bytes, point);
                })
                .OnSend((server, point, bytes) =>
                {
                    Console.WriteLine($"服務端發送資料:目标[{point.ToString()}]資料:{Encoding.UTF8.GetString(bytes)}");
                })
                .OnStarted(server =>
                {
                    Console.WriteLine($"服務端啟動");
                }).BuildAsync();

            Console.ReadLine();
        }
    }
}

           
using Coldairarrow.DotNettySocket;
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace UdpSocket.Client
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var theClient = await SocketBuilderFactory.GetUdpSocketBuilder()
                .OnClose(server =>
                {
                    Console.WriteLine($"用戶端關閉");
                })
                .OnException(ex =>
                {
                    Console.WriteLine($"用戶端異常:{ex.Message}");
                })
                .OnRecieve((server, point, bytes) =>
                {
                    Console.WriteLine($"用戶端:收到來自[{point.ToString()}]資料:{Encoding.UTF8.GetString(bytes)}");
                })
                .OnSend((server, point, bytes) =>
                {
                    Console.WriteLine($"用戶端發送資料:目标[{point.ToString()}]資料:{Encoding.UTF8.GetString(bytes)}");
                })
                .OnStarted(server =>
                {
                    Console.WriteLine($"用戶端啟動");
                }).BuildAsync();

            while (true)
            {
                await theClient.Send(Guid.NewGuid().ToString(), new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6003));
                await Task.Delay(1000);
            }
        }
    }
}

           

以上所有示例在源碼中都有,若覺得不錯請點贊加星星,希望能夠幫助到大家。

有任何問題請及時回報或加群交流

QQ群1:(已滿)

QQ群2:579202910

繼續閱讀