直接上代碼

/// <summary> /// WebSocket Handler /// </summary> public class QWebSocketHandler { private WebSocket _websocket; /// <summary> /// 用戶名 /// </summary> public string User { get; set; } /// <summary> /// webSocket 連接關閉 /// </summary> public event EventHandler Closed; /// <summary> /// webSocket 連接接受信息 /// </summary> public event EventHandler<string> Received; /// <summary> /// webSocket 連接成功 /// </summary> public event EventHandler<string> Opened; /// <summary> /// webSocket 請求連接 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task ProcessRequest(AspNetWebSocketContext context) { _websocket = context.WebSocket; var login = context.User.Identity.Name; User = login; Opened?.Invoke(this, login); while(true) { var buffer = new ArraySegment<byte>(new byte[1024]); var receivemsg = await _websocket.ReceiveAsync(buffer, System.Threading.CancellationToken.None); if(receivemsg.MessageType == WebSocketMessageType.Close) { await _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "connect colsed", CancellationToken.None); Closed?.Invoke(this, EventArgs.Empty); break; } if(_websocket.State == WebSocketState.Open) { string remsg = Encoding.UTF8.GetString(buffer.Array, 0, receivemsg.Count); Received?.Invoke(this, remsg); } } } /// <summary> /// 向當前連接發送消息 /// </summary> /// <param name="msg">消息內容</param> /// <returns></returns> public async Task<bool> SendMSG(string msg) { if (_websocket == null || _websocket.State != WebSocketState.Open) { throw new Exception("the web socket is not connected"); } var sebyte = Encoding.UTF8.GetBytes(msg); var sebuffer = new ArraySegment<byte>(sebyte); await _websocket.SendAsync(sebuffer, WebSocketMessageType.Text, true, CancellationToken.None); return true; } /// <summary> /// 關閉當前webSocket連接 /// </summary> public void Close() { if (_websocket == null || _websocket.State == WebSocketState.Closed || _websocket.State == WebSocketState.Aborted) { return; } _websocket.Abort(); } } /// <summary> /// 用戶離線消息池 /// </summary> public class MessagePool { /// <summary> /// 用戶 /// </summary> public string User { get; set; } /// <summary> /// 消息集合 /// </summary> public ConcurrentQueue<OffMessage> MessageS { get; set; } } /// <summary> /// 用戶離線消息 /// </summary> public class OffMessage:MessageTemplate { /// <summary> /// 消息失效時間 /// </summary> public DateTime ValidTime { get; set; } } /// <summary> /// 消息實體 /// </summary> public class MessageTemplate { /// <summary> /// 接受消息的用戶Login /// </summary> public string ToUser { get; set; } /// <summary> /// 發送消息的用戶Login /// </summary> public string FromUser { get; set; } /// <summary> /// 消息內容 /// </summary> public MessageContent MsgContent { get; set; } } /// <summary> /// 消息內容體實體模型 /// </summary> public class MessageContent { /// <summary> /// 標題 /// </summary> public string Title { get; set; } /// <summary> /// 內容 /// </summary> public string Content { get; set; } /// <summary> /// 日期 /// </summary> public DateTime Time { get; set; } }

/// <summary> /// webSocket服務 /// </summary> public class QWebSocketService { private static ConcurrentDictionary<string, QWebSocketHandler> _websockets = new ConcurrentDictionary<string, QWebSocketHandler>(); /// <summary> /// 用戶離線消息池 /// 用戶上線直接發送消息給用戶 /// 離線消息僅保留3天,72小時 /// </summary> private static ConcurrentQueue<MessagePool> UserMessageS = new ConcurrentQueue<MessagePool>(); /// <summary> /// 連接websocket /// </summary> /// <param name="Login"></param> /// <param name="Token"></param> /// <returns></returns> public static HttpResponseMessage Connect(System.Web.HttpContext context, string Login) { //如果用戶存在於連接池則更新 webSocket連接信息,否則新建連接池 var handler = new QWebSocketHandler(); handler.Received -= Socket_Received; handler.Received += Socket_Received; handler.Closed -= Socket_Closed; handler.Closed += Socket_Closed; handler.Opened -= Socket_Opened; handler.Opened += Socket_Opened; if (_websockets.Keys.Contains(Login)) { var inhandler = _websockets[Login]; inhandler.Close(); _websockets[Login] = handler; } else { _websockets.TryAdd(Login, handler); } context.User = new System.Security.Principal.GenericPrincipal(new System.Security.Principal.GenericIdentity(Login), null); context.AcceptWebSocketRequest(handler); return new HttpResponseMessage(System.Net.HttpStatusCode.SwitchingProtocols); } /// <summary> /// 清理過期消息 /// </summary> private static void ClearUserMessage() { var validuser = new ConcurrentQueue<MessagePool>(); foreach (var msg in UserMessageS) { var valid = new ConcurrentQueue<OffMessage>(); foreach (var msgcontent in msg.MessageS) { if ((DateTime.Now - msgcontent.ValidTime).TotalHours < 72) { valid.Enqueue(msgcontent); } } msg.MessageS = valid; if (!valid.IsEmpty) { validuser.Enqueue(msg); } } UserMessageS = validuser; } /// <summary> /// Insert send to offline user's message in messagepool /// </summary> /// <param name="msg"></param> private static void AddUserMessage(MessageTemplate msg) { if (UserMessageS.Any(q => q.User == msg.ToUser)) { //存在離線用戶離線消息 var innermsg = UserMessageS.FirstOrDefault(q => q.User == msg.ToUser); OffMessage offmessage = new OffMessage() { ToUser = msg.ToUser, FromUser = msg.FromUser, MsgContent = msg.MsgContent, ValidTime = DateTime.Now }; innermsg.MessageS.Enqueue(offmessage); } else { //不存在離線用戶消息 OffMessage offMessage = new OffMessage() { MsgContent = msg.MsgContent, FromUser = msg.FromUser, ToUser = msg.ToUser, ValidTime = DateTime.Now }; ConcurrentQueue<OffMessage> msgs = new ConcurrentQueue<OffMessage>(); msgs.Enqueue(offMessage); MessagePool usermessage = new MessagePool() { User = msg.ToUser, MessageS = msgs }; UserMessageS.Enqueue(usermessage); } } private static async Task SendOffMessage(QWebSocketHandler socket, string login) { //有離線消息則發送 await Task.Delay(2000); //異步等待2秒發送離線消息 var msgs = UserMessageS.FirstOrDefault(q => q.User == login); if (msgs != null) { var sended = new ConcurrentQueue<OffMessage>(); foreach (var omsg in msgs.MessageS) { var send = await socket.SendMSG(omsg.MsgContent.ToString()); if (!send) { send.Equals(omsg); } } msgs.MessageS = sended; } ClearUserMessage();//清理過期離線消息 } /// <summary> /// 向指定用戶發送消息 /// </summary> /// <param name="Login"></param> /// <param name="msg"></param> /// <returns></returns> public static async Task<bool> SendMSG(MessageTemplate msg) { if (_websockets.Any(q => q.Key == msg.ToUser)) { var socket = _websockets[msg.ToUser]; if (socket == null) { //用戶不在線,消息加入離線 AddUserMessage(msg); return false; } var str = JsonConvert.SerializeObject(msg.MsgContent); return await socket.SendMSG(str); } else { //用戶不在線,消息加入離線 AddUserMessage(msg); return false; } } private static void Socket_Opened(object sender, string login) { //連接后,發送離線消息 SendOffMessage((QWebSocketHandler)sender, login); } /// <summary> /// webSocket 接收消息 /// </summary> /// <param name="sender"></param> /// <param name="msg"></param> private static void Socket_Received(object sender, string msg) { } /// <summary> /// webSocket 客戶端關閉 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void Socket_Closed(object sender, EventArgs e) { var socket = (QWebSocketHandler)sender; var csocket = _websockets.FirstOrDefault(q => q.Value == socket); _websockets.TryRemove(csocket.Key, out socket); } } public static class HttpContextExtension { public static void AcceptWebSocketRequest(this HttpContext context, QWebSocketHandler handler) { context.AcceptWebSocketRequest(handler.ProcessRequest); } }

/// <summary> /// webSocket 消息管理, /// 請使用WebSocket協議請求: /// ws://server/api/msger/{login}/{token} /// {login}為當前用戶名;{token}為當前用戶登陸的有效token /// </summary> [RoutePrefix("api/msger")] public class MessageController : LoanAPI { private DBContext db = new DBContext(); /// <summary> /// 請求webSocket連接 /// </summary> /// <param name="login"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet] [Route("connect/{login}/{token}")] [AllowAnonymous] public HttpResponseMessage Connect(string login, string token) { var user = db.SYS_User.FirstOrDefault(q => q.Login == login && q.Token == token); if (user == null) { return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Login is Not Valid"); } else { if(HttpContext.Current.IsWebSocketRequest) { return QWebSocketService.Connect(HttpContext.Current, login); } else { return Request.CreateErrorResponse(HttpStatusCode.MethodNotAllowed, "Is Not WebSocekt Request"); } } } /// <summary> /// 向用戶發送消息,正常的http請求 /// </summary> /// <param name="msg"></param> /// <returns></returns> [HttpPost] [Route("send")] public async System.Threading.Tasks.Task<ActionResult<bool>> SendMSGAsync([FromBody] MessageTemplate msg) { var sended = await QWebSocketService.SendMSG(msg); return new ActionResult<bool>(sended); } }
js Common
(function ($) { $.config = { url: '', //鏈接地址 token: '',// 通訊key }; $.init = function (config) { this.config = config; return this; }; /** * 連接webcocket */ $.connect = function () { var protocol = (window.location.protocol === 'http:') ? 'ws:' : 'wss:'; this.host = protocol + this.config.url; this.protocols = this.config.token; window.WebSocket = window.WebSocket || window.MozWebSocket; if (!window.WebSocket) { // 檢測瀏覽器支持 this.error('Error: WebSocket is not supported .'); return; } this.socket = new WebSocket(this.host, [this.protocols]); // 創建連接並注冊響應函數 this.socket.onopen = function () { $.onopen(); }; this.socket.onmessage = function (message) { $.onmessage(message); }; this.socket.onclose = function () { $.onclose(); $.socket = null; // 清理 }; this.socket.onerror = function (errorMsg) { $.onerror(errorMsg); } return this; } /** * 自定義異常函數 * @param {Object} errorMsg */ $.error = function (errorMsg) { this.onerror(errorMsg); } /** * 消息發送 */ $.send = function (message) { if (this.socket) { this.socket.send(message); return true; } this.error('please connect to the server first !!!'); return false; } $.close = function () { if (this.socket !== undefined && this.socket !== null) { this.socket.close(); } else { this.error("this socket is not available"); } } /** * 消息回調 * @param {Object} message */ $.onmessage = function (message) { console.log(message) } /** * 鏈接回調函數 */ $.onopen = function () { console.log('連接成功') } /** * 關閉回調 */ $.onclose = function () { console.log('連接關閉'); } /** * 異常回調 */ $.onerror = function (error) { console.log(error); } })(ws = {});