Creating “Login form”

We use here simple form where user can insert his or her preferred nick name for chat. To keep us focused on WebSocket stuff we don’t add any logic or checks in this point. Let’s add view called InsertUserName.cshtml under Home folder.


<form action="@Url.Action("Index")" method="post">
    <input type="text" placeholder="Insert user name" name="userName" />
    <input type="submit" value="Eńter" />
</form>

There will be another view for chat room and we will come back to it later when web sockets related code is done. Index() methods of home controller look like this.


[HttpGet]
public IActionResult Index()
{
    return View("InsertUserName");
} [HttpPost]
public IActionResult Index(string username)
{
    return View("Index", username);
}

If request method is GET then we show nick name form and if request method is POST we will show chat room view.

WebSockets middleware

Now let’s write ASP.Core middleware for WebSocket. To keep things simple I mixed together custom WebSocket middleware and custom WebSocket connection manager from Radu Matei’s post Creating a WebSockets middleware for ASP .NET Core. I like the work Radu has done but here we will keep things as small as possible. To get better understanding of WebSockets I suggest you to go through Radu’s post.

NB! To use WebSockets in ASP.NET Core project add reference to Microsoft.AspNetCore.WebSockets NuGet package!

In breaf, this is what our WebSocket middleware class does:

  1. Keep concurrent dictionary with connected WebSockets (this is needed for message broadcast)
  2. Read messages from WebSocket and broadcast there to all known WebSockets
  3. Try to keep WebSockets dictionary as clean as possible

Here is the WebSocket middleware class.


public class ChatWebSocketMiddleware
{
    private static ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>();     private readonly RequestDelegate _next;     public ChatWebSocketMiddleware(RequestDelegate next)
    {
        _next = next;
    }     public async Task Invoke(HttpContext context)
    {
        if (!context.WebSockets.IsWebSocketRequest)
        {
            await _next.Invoke(context);
            return;
        }         CancellationToken ct = context.RequestAborted;
        WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync();
        var socketId = Guid.NewGuid().ToString();         _sockets.TryAdd(socketId, currentSocket);         while (true)
        {
            if (ct.IsCancellationRequested)
            {
                break;
            }             var response = await ReceiveStringAsync(currentSocket, ct);
            if(string.IsNullOrEmpty(response))
            {
                if(currentSocket.State != WebSocketState.Open)
                {
                    break;
                }                 continue;
            }             foreach (var socket in _sockets)
            {
                if(socket.Value.State != WebSocketState.Open)
                {
                    continue;
                }                 await SendStringAsync(socket.Value, response, ct);
            }
        }         WebSocket dummy;
        _sockets.TryRemove(socketId, out dummy);         await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
        currentSocket.Dispose();
    }     private static Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
    {
        var buffer = Encoding.UTF8.GetBytes(data);
        var segment = new ArraySegment<byte>(buffer);
        return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
    }     private static async Task<string> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default(CancellationToken))
    {
        var buffer = new ArraySegment<byte>(new byte[8192]);
        using (var ms = new MemoryStream())
        {
            WebSocketReceiveResult result;
            do
            {
                ct.ThrowIfCancellationRequested();                 result = await socket.ReceiveAsync(buffer, ct);
                ms.Write(buffer.Array, buffer.Offset, result.Count);
            }
            while (!result.EndOfMessage);             ms.Seek(0, SeekOrigin.Begin);
            if (result.MessageType != WebSocketMessageType.Text)
            {
                return null;
            }             // Encoding UTF8: https://tools.ietf.org/html/rfc6455#section-5.6
            using (var reader = new StreamReader(ms, Encoding.UTF8))
            {
                return await reader.ReadToEndAsync();
            }
        }
    }
}

Before using middleware we have to introduce it to request pipeline in Startup class of web application. We do it in configure method before initializing MVC.


app.UseWebSockets();
app.UseMiddleware<ChatWebSocketMiddleware>(); app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

NB! It’s very important to add WebSockets before MVC. WebSockets middleware checks in the beginning if request is for WebSocket. If it is WebSocket request then middleware starts handling it. If MVC is added to pipeline before then MVC will handle all the requests and our WebSocket middleware is never used.

Chat room view

Now let’s add chat room view. We will use Index view of home controller for chat room. Chat room view initializes WebSocket connection and starts listening to it. If user writes something to chat box and presses Enter then message is sent to server over WebSocket and WebSocket middleware will broadcast it to all WebSocket clients it knows.


@model string
@{
    ViewData["Title"] = "Home Page";
}
<style>
    body {margin:0px; padding:0px;}
    .msg {
        position: absolute;
        top: ;
        bottom: 30px;
        border: 1px solid green;
        margin-bottom: auto;
        display:block;
        overflow: scroll;
        width:100%;
        white-space:nowrap;
    }
</style>
<div class="msg">
    <div style="position:absolute; bottom:;" id="msgs"></div>
</div> <div style="position:absolute;height:20px;bottom:10px;left:; display:block;width:100%">
    <input type="text" style="max-width:unset;width:100%;max-width:100%" id="MessageField" placeholder="type message and press enter" />
</div> @section Scripts {
    <script>
    $(function () {
        var userName = '@Model';         var protocol = location.protocol === "https:" ? "wss:" : "ws:";
        var wsUri = protocol + "//" + window.location.host;
        var socket = new WebSocket(wsUri);
        socket.onopen = e => {
            console.log("socket opened", e);
        };         socket.onclose = function (e) {
            console.log("socket closed", e);
        };         socket.onmessage = function (e) {
            console.log(e);
            $('#msgs').append(e.data + '<br />');
        };         socket.onerror = function (e) {
            console.error(e.data);
        };         $('#MessageField').keypress(function (e) {
            if (e.which != 13) {
                return;
            }             e.preventDefault();             var message = userName + ": " + $('#MessageField').val();
            socket.send(message);
            $('#MessageField').val('');
        });
    });
    </script>
}

Now let’s build application and run it.

WebSocket chat room in action

The screenshot below shows how our chat room looks like. It’s extremely primitive and simple but it works. There’s room enough for improvements but this is the fun I leave to all my dear readers.

Wrapping up

Using WebSockets in ASP.NET Core is simple. Without any additional libraries we can use the one by Microsoft. We had to write custom middleware class for WebSocket communication and in our case the class came pretty small. We used concurrent dictionary as a WebSockets cache and this enabled us to broadcast messages over sockets. Our solution is very primitive and simple, there is a lot of room for improvements but as a proof of concept it works well.

ASP.NET Core Building chat room using WebSocket的更多相关文章

  1. Real-time chart using ASP.NET Core and WebSocket

    Solution in glance The following diagram illustrates our solution where IoT device reports readings ...

  2. 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 ...

  3. 关于ASP.NET Core WebSocket实现集群的思考

    前言 提到WebSocket相信大家都听说过,它的初衷是为了解决客户端浏览器与服务端进行双向通信,是在单个TCP连接上进行全双工通讯的协议.在没有WebSocket之前只能通过浏览器到服务端的请求应答 ...

  4. WebSocket in ASP.NET Core

    一.WebSocket WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP有1.1和1.0 ...

  5. 网络游戏开发-服务器(01)Asp.Net Core中的websocket,并封装一个简单的中间件

    先拉开MSDN的文档,大致读一遍 (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets) WebSocket 是一 ...

  6. 快速搭建CentOS+ASP.NET Core环境支持WebSocket

    环境:CentOS 7.x,.net core 2 以下.net core 2安装操作为官方方法.如果你使用Docker,那么更简单了,只需要docker pull microsoft/dotnet就 ...

  7. 在Asp.net Core中使用中间件来管理websocket

    介绍 ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理.但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容. ...

  8. ASP.NET Core 集成 WebSocket

    1. 环境 AspNetCore Web 2.0 (MVC) Windows 10 IIS 10 Express/IIS VS 2017 2.如何配置 在已有的或者新创建的 AspNet Core M ...

  9. ASP.NET Core 中的 WebSocket 支持(转自MSDN)

    本文介绍 ASP.NET Core 中 WebSocket 的入门方法. WebSocket (RFC 6455) 是一个协议,支持通过 TCP 连接建立持久的双向信道. 它用于从快速实时通信中获益的 ...

随机推荐

  1. Python协程(真才实学,想学的进来)

    真正有知识的人的成长过程,就像麦穗的成长过程:麦穗空的时候,麦子长得很快,麦穗骄傲地高高昂起,但是,麦穗成熟饱满时,它们开始谦虚,垂下麦芒. --蒙田<蒙田随笔全集> *** 上篇论述了关 ...

  2. Python股票分析系列——自动获取标普500股票列表.p5

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第5部分.在本教程和接下来的几节中,我们将着手研究如何为更多公司提供大量的定价信息,以及如何一次 ...

  3. gulp + gulp-better-rollup + rollup 构建 ES6 开发环境

    gulp + gulp-better-rollup + rollup 构建 ES6 开发环境 关于 Gulp 就不过多啰嗦了.常用的 js 模块打包工具主要有 webpack.rollup 和 bro ...

  4. 初次接触CSS变量

    本文的目的主要是展示CSS变量是如何工作的.随着Web应用程序变得越来越大,CSS变得越来越大,越来越多,而且很多时候都很乱,在良好的上下文中使用CSS变量,为您提供重用和轻松更改重复出现的CSS属性 ...

  5. 【深度学习】一文读懂机器学习常用损失函数(Loss Function)

    最近太忙已经好久没有写博客了,今天整理分享一篇关于损失函数的文章吧,以前对损失函数的理解不够深入,没有真正理解每个损失函数的特点以及应用范围,如果文中有任何错误,请各位朋友指教,谢谢~ 损失函数(lo ...

  6. H5 29-div和span标签

    29-div和span标签 --> 努力到无能为力, 拼搏到感动自己 --> 我是div 我是div 我是span 我是span --> --> 我是段落 我是标题 --> ...

  7. Truncated Power Method for Sparse Eigenvalue Problems

    目录 算法 k的选择 \(x\)的初始化 代码 抱歉,真的没怎么看懂,当然,估计和我现在没法静下心来好好看也有关系. 算法 想法非常非常简单吧,就是在原来幂法的基础上,每次迭代的时候再加个截断.当然, ...

  8. Factors of Factorial AtCoder - 2286 (N的阶乘的因子个数)(数论)

    Problem Statement You are given an integer N. Find the number of the positive divisors of N!, modulo ...

  9. Sagheer and Nubian Market CodeForces - 812C (二分)

    On his trip to Luxor and Aswan, Sagheer went to a Nubian market to buy some souvenirs for his friend ...

  10. ios 后台下载,断点续传总结

    2018年12月05日 16:09:00 weixin_34101784 阅读数:5 https://blog.csdn.net/weixin_34101784/article/details/875 ...