WebSocket In ASP.NET Core(二)
.NET-Core Series
- Server in ASP.NET-Core
 - DI in ASP.NET-Core
 - Routing in ASP.NET-Core
 - Error Handling in ASP.NET-Core
 - WebSocket in ASP.NET-Core(一)
 - WebSocket in ASP.NET-Core(二)
 - To Be Continue...
 
Introduce
  上篇博文中,介绍了WebSocket 的基本原理,以及一个简单的Demo用来对其有一个大致的认识。这篇博文讲的是我们平常在网站上可能会经常遇到的——实时聊天,本文就是来讲在.NET-Core 使用WebSocket来实现一个“乞丐版”的在线实时聊天Demo。
关键词:Middleware,Real-Time,WebSocket
Before You Read.
 这个和我们上一篇博文中Demo 有何不同的呢?有何共同之处呢?
 相同的点是,都是网页作为客户端,使用JavaScript 来发送和接收请求,.NET-Core 服务端接收到请求,发送该请求给客户端。
不同的地方呢,就像我下面这张图这样:
一次同时有多个客户端在,所以应该很清楚,我们只要在上面例子的基础上,对当前的已存在的Socket进行轮询,发送回应即可达到我们想要的效果。
Create WebSocket Middleware
 在上个Demo的例子中,我们直接在Startup 的Configure 中直接写接受WebSocket请求。这次我们换成Middleware形式来处理。在写Middleware之前,在之前的介绍中,我们知道,需要有多个WebSocket,那么肯定需要一些对WebSocket 的Get/Set 处理。我简单的写了一个下面WebScoket Manger Class
//WebSocketManager.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketManage
{
    public class WSConnectionManager
    {
        private static ConcurrentDictionary<string, WebSocket> _socketConcurrentDictionary = new ConcurrentDictionary<string, WebSocket>();
        public void AddSocket(WebSocket socket)
        {
            _socketConcurrentDictionary.TryAdd(CreateGuid(), socket);
        }
        public async Task RemoveSocket(WebSocket socket)
        {
            _socketConcurrentDictionary.TryRemove(GetSocketId(socket), out WebSocket aSocket);
            await aSocket.CloseAsync(
                closeStatus: WebSocketCloseStatus.NormalClosure,
                statusDescription: "Close by User",
                cancellationToken: CancellationToken.None).ConfigureAwait(false);
        }
        public string GetSocketId(WebSocket socket)
        {
            return _socketConcurrentDictionary.FirstOrDefault(k => k.Value == socket).Key;
        }
        public ConcurrentDictionary<string, WebSocket> GetAll()
        {
            return _socketConcurrentDictionary;
        }
        public string CreateGuid()
        {
            return Guid.NewGuid().ToString();
        }
    }
}
上面主要是对 WebSocket 进行了简单的存取操作进行了封装。下面也把WebSocket 的Send 和Recieve 操作进行了封装。
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketManage
{
    public class WSHandler
    {
        protected WSConnectionManager _wsConnectionManager;
        public WSHandler(WSConnectionManager wSConnectionManager)
        {
            _wsConnectionManager = wSConnectionManager;
        }
        public async Task SendMessageAsync(
            WebSocket socket,
            string message,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            var buffer = Encoding.UTF8.GetBytes(message);
            var segment = new ArraySegment<byte>(buffer);
            await socket.SendAsync(segment, WebSocketMessageType.Text, true, cancellationToken);
        }
        public async Task<string> RecieveAsync(WebSocket webSocket, CancellationToken cancellationToken)
        {
            var buffer = new ArraySegment<byte>(new byte[1024 * 8]);
            using (var ms = new MemoryStream())
            {
                WebSocketReceiveResult result;
                do
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    result = await webSocket.ReceiveAsync(buffer, cancellationToken);
                    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 await reader.ReadToEndAsync();
                }
            }
        }
    }
}
有了上面两个辅助类之后,接下来就可以写我们自己的RealTimeWebSocketMiddlerware 了,
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocketManage;
namespace Robert.Middleware.WebSockets
{
    public class RealTimeWSMiddleware
    {
        private readonly RequestDelegate _next;
        private WSConnectionManager _wSConnectionManager { get; set; }
        private WSHandler _wsHanlder { get; set; }
        public RealTimeWSMiddleware(
            RequestDelegate next,
            WSConnectionManager wSConnectionManager,
            WSHandler wsHandler)
        {
            _next = next;
            _wSConnectionManager = wSConnectionManager;
            _wsHanlder = wsHandler;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            if (httpContext.WebSockets.IsWebSocketRequest)
            {
                var cancellationToken = httpContext.RequestAborted;
                var currentWebSocket = await httpContext.WebSockets.AcceptWebSocketAsync();
                _wSConnectionManager.AddSocket(currentWebSocket);
                while (true)
                {
                    if (cancellationToken.IsCancellationRequested) break;
                    var response = await _wsHanlder.ReceiveAsync(currentWebSocket, cancellationToken);
                    if (string.IsNullOrEmpty(response) && currentWebSocket.State != WebSocketState.Open) break;
                    foreach (var item in _wSConnectionManager.GetAll())
                    {
                        if (item.Value.State == WebSocketState.Open)
                        {
                            await _wsHanlder.SendMessageAsync(item.Value, response, cancellationToken);
                        }
                        continue;
                    }
                }
                await _wSConnectionManager.RemoveSocket(currentWebSocket);
            }
            else
            {
                await _next(httpContext);
            }
        }
    }
}
 其实到这里,核心部分已经讲完了,接下来就是页面显示,发现信息,交互的问题了。在客户端还是像上篇文章中的一样,直接使用 JavaScript 发送WebScoket请求。
下面主要演示一下效果,在上篇博文的基础上,加上了用户名。
<script>
    $(function () {
        var protocol = location.protocol === "https:" ? "wss:" : "ws:";
        var Uri = protocol + "//" + window.location.host + "/ws";
        var socket = new WebSocket(Uri);
        socket.onopen = e => {
            console.log("socket opened", e);
        };
        socket.onclose = function (e) {
            console.log("socket closed", e);
        };
        //function to receive from server.
        socket.onmessage = function (e) {
            console.log("Message:" + e.data);
            $('#msgs').append(e.data + '<br />');
        };
        socket.onerror = function (e) {
            console.error(e.data);
        };
    });
</script>
当写好了页面文件后,运行站点。最终运行的效果图,界面以实用为主,不求美观。
WebSocket In ASP.NET Core(二)的更多相关文章
- WebSocket in ASP.NET Core
		
一.WebSocket WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP有1.1和1.0 ...
 - WebSocket In ASP.NET Core(一)
		
.NET-Core Series Server in ASP.NET-Core DI in ASP.NET-Core Routing in ASP.NET-Core Error Handling in ...
 - K8S+GitLab-自动化分布式部署ASP.NET Core(二) ASP.NET Core  DevOps
		
一.介绍 前一篇,写的K8S部署环境的文章,简单的介绍下DevOps(Development和Operations的组合词),高效交付, 自动化流程,来减少软件开发人员和运维人员的沟通.Martin ...
 - asp.net core 二 Nginx Supervisor 负载,监听
		
ASP.NET Core负载均衡集群搭建(CentOS7+Nginx+Supervisor+Kestrel) asp.net core在linux运行下,一但命令行退出 ...
 - ASP.NET Core (二):入门
		
上一篇:ASP.NET Core(一):简介 下一篇:(待续) 英文原版:Getting Started 1. 安装 .NET Core 2. 创建 .NET Core 项目 在命令提示符窗口输入命令 ...
 - asp.net core系列 70 即时通迅-WebSocket+Redis发布订阅
		
一.概述 在asp.net core 中可以用WebSocket 或asp.net core SignalR来开发即时通迅.在项目中由于开发前后端分离,对于SignalR前端技术人员不想依赖juqer ...
 - 使用websocket连接(对接)asp.net core signalr
		
使用通用websocket连接asp.net core signalr 一.背景介绍 signalr的功能很强大,可以为我们实现websocket服务端节省不少的时间.但是可能由于不同的环境,我们在对 ...
 - 在ASP.NET Core下使用SignalR技术
		
一.前言 上次我们讲到过如何在ASP.NET Core中使用WebSocket,没有阅读过的朋友请参考 WebSocket in ASP.NET Core 文章 .这次的主角是SignalR它为我们提 ...
 - ASP.NET Core (一):简介
		
下一篇:ASP.NET Core(二):入门 英文原版:Introduction to ASP.NET Core 关于ASP.NET Core ASP.NET Core 是一个全新的开源.跨平台框架, ...
 
随机推荐
- MySQL (八)
			
1 事务 需求:有一张银行账户表,A用户给B用户转账,A账户先减少,B账户增加,但是A操作完之后断电了. 解决方案:A减少钱,但是不要立即修改数据表,B收到钱之后,同时修改数据表. 事务:一系列要发生 ...
 - HTML5-移动开发常用技巧与弹性布局的使用
			
一.移动开发常用技巧 Viewport基本知识 设置布局Viewport的各种信息 1.width=device-width: 设置Viewport视口宽度等于设备宽度 2.initial-scale ...
 - Wo Wei Shen Me Hui Zai   cnblogs Xie Bo Ke
			
我为什么会在cnblogs上写博客.. CSDN上我上传过代码被很多网友下载过.CSDN我申请过博客.也写过几篇博客. 开源中国 我上传过代码.代码也被网友下载过.OSChina我申请过博客.也写过几 ...
 - BotVS开发基础—2.2 下限价单 交易
			
代码 import json def main(): Log("ORDER_STATE_PENDING:", ORDER_STATE_PENDING, ", ORDER_ ...
 - vue-cli脚手架npm相关文件解读(1)webpack.base.conf.js
			
系列文章传送门: 1.build/webpack.base.conf.js 2.build/webpack.prod.conf.js 3.build/webpack.dev.conf.js 4.bui ...
 - instanceof 是如何工作的
			
知其然 JavaScript 提供 instanceof 关键字判断一个对象所属的构造函数.在 js 高级编程中讲到 instanceof 的作用: instanceof 操作符,用来测试实例与原型链 ...
 - django框架(View)
			
-------------------URLconf-------------------1.设置 1.在settings.py文件中通过ROOT_URLCONF指定根级url的配置 2.urlpat ...
 - quartz源码分析——执行引擎和线程模型
			
title: quartz源码分析--执行引擎和线程模型 date: 2017-09-09 23:14:48 categories: quartz tags: [quartz, 源码分析] --- - ...
 - mysql添加外键约束变为索引
			
今天有位自己填上一坑:mysql储存引擎 原因就是数据库表引擎为:MyISAM,建立主外键关系需要是InnoDB: 解决方案:alter table table_name1 engine=inno ...
 - java伪代码《大道至简》
			
阅读<大道至简>第一章,深感作者对编程问题的精炼定义,通过对古老寓言故事<愚公移山>的引用,说明了编程的本质,即顺序,分支,循环.其中又将他们扮演的项目组织者,团队经理,编程人 ...
 
			
		