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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM