NetCore MVC WebSocket 簡易聊天室
阿新 • • 發佈:2018-12-08
前端程式碼:
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);
}