1. 程式人生 > >NetCore MVC WebSocket 簡易聊天室

NetCore MVC WebSocket 簡易聊天室

前端程式碼: 

function SocketHelper(params) {
   var options = $.extend({}, {
        uri: "ws://" + window.location.host + "//socket/Connect",
        Received: function (result) { },//接收事件
        ReceiverId: "" //接收人
   }, params);
   var socket;//物件
   var SenderId = "";//傳送人
    Connect = function () {
        // this.uri = uri;
        this.socket = new WebSocket(options.uri);
        this.socket.onopen = function (e) { _self.Open(e) };
        var _self = this;
        this.socket.onmessage = function (e) { _self.Received(e); };
        this.socket.onerror = function (e) { _self.Error(e); };
    };
    Send = function (ReceiverId, message) {
        var sendData = { "SenderId": SenderId, "ReceiverId": ReceiverId, "MessageType": "text", "Content": message };
        try {
            this.socket.send(JSON.stringify(sendData));
        } catch (e) {
            this.Connect();

        }
        
    };
    Open = function (e) {
        this.Send("Service","連線");
        console.log("socket opened", e);
    };
    Close = function (e) {
        this.socket.close();
        console.log("socket closed", e);
    };
    Received = function (e) {
        var result = JSON.parse(e.data);
        if (options.Received) {
            options.Received(result);
        }
        if (result.SenderId == "Service" && result.Content == "open")
        {
            SenderId = result.ReceiverId;
        }
       
        console.log("Received: " + result.ReceiverId);
    };
    Error = function (e) {
        console.log("Error: " + e.data);
    }
    return this;
}

 

後端實現

private static ConcurrentDictionary<string, WebSocket> _socketConnectUsers;
        /// <summary>
        /// 用於儲存線上的websocket使用者
        /// </summary>
        public static ConcurrentDictionary<string, WebSocket> SocketConnectUsers
        {
            get {
               
                return _socketConnectUsers;
            }
        }
        public const int BufferSize = 4096;
        private string _sockSenderId = "";
        WebSocket socket;
        public WebSocketHelper(WebSocket socket)
        {
            this.socket = socket;
        }
        async Task EchoLoop()
        {
          
            var buffer = new byte[1024 * 4];
            WebSocketReceiveResult result = await this.socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            if (_socketConnectUsers == null)
            {
                _socketConnectUsers = new ConcurrentDictionary<string, WebSocket>();
            }
            #region 建立websocket Id
            if (string.IsNullOrWhiteSpace(ApplicationEnvironments.DefaultSession.UserName))
            {
                _sockSenderId = Guid.NewGuid().ToString();
            }
            else
            {
                _sockSenderId = ApplicationEnvironments.DefaultSession.UserName;
            }
            #endregion
            if (!result.CloseStatus.HasValue)//第一次連線 
            {
                _socketConnectUsers.TryAdd(_sockSenderId, socket);
                await SendAsync(new WSMessageHelper {//連線成功時返回使用者id
                    SenderId="Service",
                    ReceiverId= _sockSenderId,
                     MessageType="text",
                     Content="open"
                }, WebSocketMessageType.Text, result.EndOfMessage);
            }
            while (!result.CloseStatus.HasValue)
            {
                string sendMsg= ReceiveString(buffer,result);
                WSMessageHelper wSMessage = null;
                if (!string.IsNullOrWhiteSpace(sendMsg))
                {
                    wSMessage= JSONHelper.FromJson<WSMessageHelper>(sendMsg);
                }
                if (wSMessage != null&&!string.IsNullOrWhiteSpace(wSMessage.ReceiverId)&& !wSMessage.ReceiverId.ToLower().Equals("service"))
                {
                    if (!await SendAsync(wSMessage, WebSocketMessageType.Text, true))//false表示傳送失敗
                    {
                       await SendAsync(new WSMessageHelper //,失敗時回發使用者訊息
                       {
                            SenderId = "Service",
                            ReceiverId = _sockSenderId,
                            MessageType = "text",
                            Content = "訊息傳送不成功,使用者已下線"
                        }, WebSocketMessageType.Text, result.EndOfMessage);
                    }
                }
                result = await this.socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            }
            _socketConnectUsers.TryRemove(_sockSenderId,out socket);
            await this.socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
        }

        /// <summary>
        /// 傳送訊息
        /// </summary>
        /// <param name="message">訊息物件</param>
        /// <param name="messageType">訊息型別</param>
        /// <param name="endOfMessage">是否結束訊息</param>
        /// <returns></returns>
        public async Task<bool> SendAsync(WSMessageHelper message, WebSocketMessageType messageType, bool endOfMessage)
        {
            var buffer1 = FileHelper.StringToByte("utf-8", JSONHelper.ToJson(message));
            var outgoing = new ArraySegment<byte>(buffer1, 0, buffer1.Length);
            WebSocket socket = null;
            if (_socketConnectUsers.TryGetValue(message.ReceiverId, out socket))
            {
                await socket.SendAsync(outgoing, messageType, endOfMessage, CancellationToken.None);
                return true;
            }
            return false;
          
        }

        private string ReceiveString(ArraySegment<byte> buffer,WebSocketReceiveResult result)
        {
            using (var ms = new MemoryStream())
            {
                do
                {
                    ms.Write(buffer.Array, buffer.Offset, result.Count);
                }
                while (!result.EndOfMessage);

                ms.Seek(0, SeekOrigin.Begin);
                if (result.MessageType != WebSocketMessageType.Text)
                {
                    return null;
                }

                using (var reader = new StreamReader(ms, Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }
        static async Task Acceptor(HttpContext hc, Func<Task> n)
        {
            if (!hc.WebSockets.IsWebSocketRequest)
                return;
            var socket = await hc.WebSockets.AcceptWebSocketAsync();
            var h = new WebSocketHelper(socket);
            await h.EchoLoop();
        }
        /// <summary>
        /// 路由繫結處理
        /// </summary>
        /// <param name="app"></param>
        public static void Map(IApplicationBuilder app)
        {
            app.UseWebSockets();
            app.Use(WebSocketHelper.Acceptor);
        }

訊息物件類

 public class WSMessageHelper
    {
        /// <summary>
        /// 傳送者Id
        /// </summary>
        public string SenderId { get; set; }

        /// <summary>
        /// 接受者id
        /// </summary>
        public string ReceiverId { get; set; }

        /// <summary>
        /// 訊息型別
        /// </summary>
        public string MessageType { get; set; }

        public object Content { get; set; }
    }

 在Startup類中增加以下程式碼


public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{

            app.Map("/socket/Connect", WebSocketHelper.Map);
          
}