Asp.Net WebApi使用Websocket
直接上代码
/// <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 = {});