NetCore WebSocket 即時通訊示例

CRown團隊發表於2017-12-24

1.新建Netcore Web專案

2.建立簡易通訊協議

public class MsgTemplate
    {
        public string SenderID { get; set; }
        public string ReceiverID { get; set; }
        public string MessageType { get; set; }
        public string Content { get; set; }
    }複製程式碼

SenderID傳送者ID

ReceiverID 接受者ID

MessageType 訊息型別 Text Voice 等等

Content 訊息內容

3.新增中介軟體ChatWebSocketMiddleware

  1 public class ChatWebSocketMiddleware
  2     {
  3         private static ConcurrentDictionary<string, System.Net.WebSockets.WebSocket> _sockets = new ConcurrentDictionary<string, System.Net.WebSockets.WebSocket>();
  4 
  5         private readonly RequestDelegate _next;
  6 
  7         public ChatWebSocketMiddleware(RequestDelegate next)
  8         {
  9             _next = next;
 10         }
 11 
 12         public async Task Invoke(HttpContext context)
 13         {
 14             if (!context.WebSockets.IsWebSocketRequest)
 15             {
 16                 await _next.Invoke(context);
 17                 return;
 18             }
 19             System.Net.WebSockets.WebSocket dummy;
 20 
 21             CancellationToken ct = context.RequestAborted;
 22             var currentSocket = await context.WebSockets.AcceptWebSocketAsync();
 23             //string socketId = Guid.NewGuid().ToString();
 24             string socketId = context.Request.Query["sid"].ToString();
 25             if (!_sockets.ContainsKey(socketId))
 26             {
 27                 _sockets.TryAdd(socketId, currentSocket);
 28             }
 29             //_sockets.TryRemove(socketId, out dummy);
 30             //_sockets.TryAdd(socketId, currentSocket);
 31 
 32             while (true)
 33             {
 34                 if (ct.IsCancellationRequested)
 35                 {
 36                     break;
 37                 }
 38 
 39                 string response = await ReceiveStringAsync(currentSocket, ct);
 40                 MsgTemplate msg = JsonConvert.DeserializeObject<MsgTemplate>(response);
 41 
 42                 if (string.IsNullOrEmpty(response))
 43                 {
 44                     if (currentSocket.State != WebSocketState.Open)
 45                     {
 46                         break;
 47                     }
 48 
 49                     continue;
 50                 }
 51 
 52                 foreach (var socket in _sockets)
 53                 {
 54                     if (socket.Value.State != WebSocketState.Open)
 55                     {
 56                         continue;
 57                     }
 58                     if (socket.Key == msg.ReceiverID || socket.Key == socketId)
 59                     {
 60                         await SendStringAsync(socket.Value, JsonConvert.SerializeObject(msg), ct);
 61                     }
 62                 }
 63             }
 64 
 65             //_sockets.TryRemove(socketId, out dummy);
 66 
 67             await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
 68             currentSocket.Dispose();
 69         }
 70 
 71         private static Task SendStringAsync(System.Net.WebSockets.WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
 72         {
 73             var buffer = Encoding.UTF8.GetBytes(data);
 74             var segment = new ArraySegment<byte>(buffer);
 75             return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
 76         }
 77 
 78         private static async Task<string> ReceiveStringAsync(System.Net.WebSockets.WebSocket socket, CancellationToken ct = default(CancellationToken))
 79         {
 80             var buffer = new ArraySegment<byte>(new byte[8192]);
 81             using (var ms = new MemoryStream())
 82             {
 83                 WebSocketReceiveResult result;
 84                 do
 85                 {
 86                     ct.ThrowIfCancellationRequested();
 87 
 88                     result = await socket.ReceiveAsync(buffer, ct);
 89                     ms.Write(buffer.Array, buffer.Offset, result.Count);
 90                 }
 91                 while (!result.EndOfMessage);
 92 
 93                 ms.Seek(0, SeekOrigin.Begin);
 94                 if (result.MessageType != WebSocketMessageType.Text)
 95                 {
 96                     return null;
 97                 }
 98 
 99                 using (var reader = new StreamReader(ms, Encoding.UTF8))
100                 {
101                     return await reader.ReadToEndAsync();
102                 }
103             }
104         }
105     }複製程式碼

控制只有接收者才能收到訊息
if (socket.Key == msg.ReceiverID || socket.Key == socketId)
{
     await SendStringAsync(socket.Value,JsonConvert.SerializeObject(msg), ct);
}複製程式碼

4.在Startup.cs中使用中介軟體

app.UseWebSockets();
app.UseMiddleware<ChatWebSocketMiddleware>();複製程式碼

5.建立移動端測試示例 這裡採用Ionic3執行在web端

建立ionic3專案略過 新手可點這裡檢視 或者有Angular2/4專案經驗的可直接往下看

(1) 啟動Ionic專案

當初建立ionic3專案時候遇到不少問題

比如ionic-cli初始化專案失敗 切換到預設npmorg源就好了

比如ionic serve失敗 開啟代理允許翻牆就好了

啟動後介面是這樣式的

(2) 建立聊天視窗dialog 具體佈局實現 模組載入略過 直接進入websocket實現

在這之前別忘了啟動web專案 否則會出現這樣情況 連結不到服務

(3)dialog.ts具體實現

export class Dialog {

    private ws: any;
    private msgArr: Array<any>;

    constructor(private httpService: HttpService) {

        this.msgArr = [];
    }

    ionViewDidEnter() {
        if (!this.ws) {
            this.ws = new WebSocket("ws://localhost:56892?sid=222");

            this.ws.onopen = () => {
                console.log('open');
            };

            this.ws.onmessage = (event) => {
                console.log('new message: ' + event.data);
                var msgObj = JSON.parse(event.data);
                this.msgArr.push(msgObj);;
            };

            this.ws.onerror = () => {
                console.log('error occurred!');
            };

            this.ws.onclose = (event) => {
                console.log('close code=' + event.code);
            };
        }
    }

    sendMsg(msg) {//msg為我要傳送的內容 比如"hello world"
        var msgObj = {
            SenderID: "222",
            ReceiverID: "111",
            MessageType: "text",
            Content: msg
        };
        this.ws.send(JSON.stringify(msgObj));
    }複製程式碼
ws://localhost:56892?sid=222 這是websocke服務連結地址
sid表示著我這個端的WebSocke唯一標識 找到這個key就可以找到我這個使用者端了
6.在web端也實現一個會話視窗
<div class="container" style="width:90%;margin:0px auto;border:1px solid steelblue;">
    <div class="msg">
        <div id="msgs" style="height:200px;"></div>
    </div>

    <div style="display:block;width:100%">
        <input type="text" style="max-width:unset;width:100%;max-width:100%" id="MessageField" placeholder="type message and press enter" />
    </div>
</div>複製程式碼
<script>
        $(function () {
            $('.navbar-default').addClass('on');

            var userName = '@Model';

            var protocol = location.protocol === "https:" ? "wss:" : "ws:";
            var wsUri = protocol + "//" + window.location.host + "?sid=111";
            var socket = new WebSocket(wsUri);
            socket.onopen = e => {
                console.log("socket opened", e);
            };

            socket.onclose = function (e) {
                console.log("socket closed", e);
            };

            socket.onmessage = function (e) {
                console.log(e);
                var msgObj = JSON.parse(e.data);
                $('#msgs').append(msgObj.Content + '<br />');
            };

            socket.onerror = function (e) {
                console.error(e.data);
            };

            $('#MessageField').keypress(function (e) {
                if (e.which != 13) {
                    return;
                }

                e.preventDefault();

                var message = $('#MessageField').val();

                var msgObj = {
                    SenderID:"111",
                    ReceiverID:"222",
                    MessageType: "text",
                    Content: message
                };
                socket.send(JSON.stringify(msgObj));
                $('#MessageField').val('');
            });
        });
    </script>複製程式碼

基本開發完成 接下來看看效果

7.web和webapp端對話

8.webapp傳送 web接收

9.目前就實現了這麼多 因為專案還涉及其它技術 暫時不開放原始碼了 


相關文章