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; }
    }
handler
/// <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);
        }
    }
Service
/// <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);
        }
         
    }
Controller

 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 = {});

 

posted @ 2020-08-20 11:21  netlock  阅读(2093)  评论(0编辑  收藏  举报