Logo

.Net6简单使用WebSocket

photo

2024年06月17日

1:WebSocket简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

大家可以查看菜鸟中的说明 https://www.runoob.com/html/html5-websocket.html

  WebSocket可以通过JS与服务器端建立实时的双向通信,它主要包括:2个核心方法、4个监听函数、1个核心属性。

2个核心方法:

  ① send() 向远程服务器发送信息

  ② close() 关闭与远程服务器的连接

4个监听函数:

  ① onopen 当网络连接建立成功时触发该事件
  ② onerror 当网络发生错误时触发该事件
  ③ onclose 当网络连接被关闭时触发该事件
  ④ onmessage 当websocket接收到服务器发来的消息的时触发该事件

1个核心属性:readyState,它有四种状态

  ① WebSocket.OPEN:表示与服务器已经建立好连接

  ② WebSocket.CLOSED:表示与服务器连接已经断开

  ③ WebSocket.CONNECTING:表示正在尝试与服务建立连接

  ④ WebSocket.CLOSING:表示正在关闭与服务器连接

服务器端主要用到两个方法:

  ① SendAsync() 发送信息给客户端

  ② CloseAsync() 关闭该Socket连接:

2:主要code

js部分

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<div class="container">
    <div>
        <table>
            <tr><td>ID:</td><td><input id="useid" type="text" placeholder="请输入人员ID" /></td></tr>
            <tr><td>昵称:</td><td><input id="usenike" type="text" placeholder="请输入人员昵称" /></td></tr>
            <tr><td><button id="btnJoin">join room</button> </td><td><button id="btnLeave">leave room</button></td></tr>
            <tr>
                <td>
                    <input id="touid" type="text" placeholder="请输入接收人员ID" /> 消息:<input type="text" id="txtMsg" value="" />
                </td>
                <td>
                    <button id="btnSend">单聊</button>
                </td>
            </tr>
            <tr><td><button id="btnAllsend">群聊</button></td></tr>
        </table>
        <div>
            <textarea style="height:300px;width:400px" id="msgList"></textarea>
        </div>
    </div>
</div>
@section Scripts{
    <script type="text/javascript">
        $(function () {
            var server = 'ws://localhost:5000'; //如果开启了https则这里是wss
            var WEB_SOCKET = new WebSocket(server + '/ws');
            WEB_SOCKET.onopen = function (evt) {
                console.log('Connection open ...');
                $('#msgList').val('websocket connection opened .');
            };

            WEB_SOCKET.onmessage = function (evt) {
                console.log('Received Message: ' + evt.data);
                if (evt.data) {
                    var content = $('#msgList').val();
                    content = content + '\r\n' + evt.data;

                    $('#msgList').val(content);
                }
            };

            WEB_SOCKET.onerror = function (evt) {
                if (evt) {
                    console.log(evt.data);
                }
            };

            WEB_SOCKET.onclose = function (evt) {
                console.log('Connection closed.');
                console.log("=======" + evt);
                var content = $('#msgList').val();
                content = content + '\r\n' + evt;
                $('#msgList').val(content);
                WEB_SOCKET.readyState = closed;
            };
            //加入聊天室
            $('#btnJoin').on('click', function () {
                var useid = $('#useid').val();
                var nick = $('#usenike').val();
                if (useid) {
                    var msg = {
                        Action: 7,
                        RoomId: 0,
                        Uid: useid,
                        MsgInfo: nick + "加入到群聊"
                    };
                    if (WEB_SOCKET == null || !WEB_SOCKET) {
                        WEB_SOCKET.readyState = open();
                        //WEB_SOCKET = new WebSocket(server + '/ws');
                    }
                    WEB_SOCKET.send(JSON.stringify(msg));
                }
            });
            //发送消息
            $('#btnSend').on('click', function () {
                var useid = $('#useid').val();
                var touid = $("#touid").val();
                var usenike = $('#usenike').val();
                var message = $('#txtMsg').val();
                if (message) {
                    WEB_SOCKET.send(JSON.stringify({
                        Action: 8,//8:单聊
                        MsgInfo: message,
                        nick: usenike,
                        ToUid: touid,
                        Uid: useid
                    }));
                }
            });
            //群聊
            $('#btnAllsend').on("click", function () {
                var useid = $('#useid').val();
                var touid = $("#touid").val();
                var usenike = $('#usenike').val();
                var message = $('#txtMsg').val();
                if (message) {
                    WEB_SOCKET.send(JSON.stringify({
                        Action: 9,//9:群聊
                        MsgInfo: message,
                        nick: usenike,
                        //ToUid: touid,
                        Uid: useid,
                        RoomId: 0//群ID
                    }));
                }
            });

            $('#btnLeave').on('click', function () {
                var nick = $('#usenike').val();
                var useid = $('#useid').val();
                var msg = {
                    Action: 10,//10:退出群聊
                    MsgInfo: nick + ",退出聊天室",
                    nick: usenike,
                    Uid: useid,
                    RoomId: 0//群ID
                };
                WEB_SOCKET.send(JSON.stringify(msg));
            });
        })
    </script>
}

中间件主要code

using System.Net.WebSockets;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
namespace WebApp
{
    public class WebSocketHandlerMiddleware : IMiddleware
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            if (context.Request.Path == "/ws")
            {
                if (context.WebSockets.IsWebSocketRequest)
                {
                    try
                    {
                        WebSocket websocket = await context.WebSockets.AcceptWebSocketAsync();
                        if (websocket != null)
                        {
                            var wsclient = new WebSocketClient
                            {
                                Uid = "",// clientId,
                                WebSocket = websocket
                            };
                            await Handle(wsclient);
                        }
                    }
                    catch (Exception ex)
                    {
                        await context.Response.WriteAsync(ex.Message);
                    }
                }
                else
                {
                    context.Response.StatusCode = 404;
                }
            }
            else
            {
                await next(context);
            }
        }

        private async Task Handle(WebSocketClient wsclient)
        {
            WebSocketClientCollection.AddClient(wsclient);

            WebSocketReceiveResult clientData = null;
            do
            {
                var buffer = new byte[1024 * 10];
                clientData = await wsclient.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), CancellationToken.None);
                if (clientData.MessageType == WebSocketMessageType.Text)
                {
                    string msgStr = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
                    var useMsg = JsonConvert.DeserializeObject<UseMsg>(msgStr);
                    useMsg.Uid = useMsg.Uid;//这里暂时使用系统的 guid
                    wsclient.Uid = useMsg.Uid;
                    await HandlessMessageAsync(useMsg);//除了具体的消息
                }

            } while (!clientData.CloseStatus.HasValue);
            WebSocketClientCollection.RemoveClient(wsclient.Uid);
            //log一下
        }

        private async Task HandlessMessageAsync(UseMsg useMsg)
        {
            switch (useMsg.Action)
            {
                case 0:; break;
                case 1:; break;
                case 2:; break;
                case 3:; break;
                case 4:; break;
                case 5:; break;
                case 6:; break;
                case 7://加入到房间
                       //写入到用户的房间表中,一个房间一行主数据
                    var client = WebSocketClientCollection.GetCLient(useMsg.Uid);
                    await JoinToRoom(client, useMsg);
                    ; break;
                case 8://单聊

                    //var buffer = new ArraySegment<byte>();
                    //await client.WebSocket.SendAsync(buffer, WebSocketMessageType.Text, WebSocketMessageFlags.EndOfMessage, CancellationToken.None)
                    //    ; break;
                    var receClient = WebSocketClientCollection.GetCLient(useMsg.ToUid.ToString());
                    if (receClient != null)
                    {
                        string msgStr = useMsg.MsgInfo;
                        byte[] by = Encoding.UTF8.GetBytes(msgStr);
                        var buffer = new ArraySegment<byte>(by, 0, by.Length);
                        await receClient.WebSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    break;

                case 9://群聊
                    var receClients = WebSocketClientCollection.GetAllClient();
                    if (receClients != null)
                    {
                        string msgStr9 = useMsg.MsgInfo;
                        byte[] by9 = Encoding.UTF8.GetBytes(msgStr9);
                        var buffer9 = new ArraySegment<byte>(by9, 0, by9.Length);
                        foreach (var item in receClients)//也可以排除发送者自己
                        {
                            await item.WebSocket.SendAsync(buffer9, WebSocketMessageType.Text, true, CancellationToken.None);
                        }
                    }
                    break;
                case 10://离开聊天室
                    string get_uid = useMsg.Uid.ToString();
                    var receClient10 = WebSocketClientCollection.GetCLient(get_uid);
                    if (receClient10 != null)
                    {
                        //string msgStr10 = useMsg.MsgInfo;
                        //byte[] by10 = Encoding.UTF8.GetBytes(msgStr10);
                        //var buffer10 = new ArraySegment<byte>(by10, 0, by10.Length);
                        //await receClient10.WebSocket.SendAsync(buffer10, WebSocketMessageType.Text, true, CancellationToken.None);
                        WebSocketClientCollection.RemoveClient(receClient10.Uid);
                        
                        await receClient10.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure,string.Empty, CancellationToken.None);
                    }
                    ; break;
                default:
                    break;
            }
        }

        private async Task<bool> JoinToRoom(WebSocketClient client, UseMsg msg)
        {
            string uid = msg.Uid;
            if (client!=null)
                client.Uid = uid;
            if (WebSocketClientCollection.GetCLient(uid) == null)
                WebSocketClientCollection.AddClient(client);
            string msgStr = msg.MsgInfo;
            byte[] by = Encoding.UTF8.GetBytes(msgStr);
            var buffer = new ArraySegment<byte>(by, 0, by.Length);
            //所有人收到消息
            var clients = WebSocketClientCollection.GetAllClient();
            if (clients != null)
            {
                clients = clients.Where(c => !string.IsNullOrEmpty(c.Uid)).ToList();
                foreach (var c in clients)
                {
                    await c.WebSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                }
            }
            return true;
        }
    }
}

入口处相关code

using WebApp;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<WebSocketHandlerMiddleware>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseWebSockets();
app.UseMiddleware<WebSocketHandlerMiddleware>();
app.UseAuthorization();

app.MapControllers();
app.Run();

相关模型实体

namespace WebApp
{
    public class UseMsg
    {
        /// <summary>
        /// 系统当前聊天用户的ID
        /// </summary>
        public string Uid { get; set; }
        /// <summary>
        ///默认:人员ID
        /// </summary>
        public int ToUid { get; set; }
        /// <summary>
        ///当前 选择群或者叫房间的ID
        /// </summary>
        public int RoomId { get; set; }
        /// <summary>
        /// 消息类型,默认0:文字,1:图片,2:视频;3:音频;4:震动;5:离线消息;6:系统表情包(如图片,gif等);7:加入聊天室;8:单聊;9:群聊;10:离开
        /// </summary>
        public int Action { get; set; }
        public string MsgInfo { get; set; }

    }
}
using System.Net.WebSockets;

namespace WebApp
{
    public class WebSocketClient
    {
        public string Uid { get; set; }
        /// <summary>
        /// 当时链接的socket
        /// </summary>
        public WebSocket WebSocket { get; set; }
    }
}
using System.Linq;

namespace WebApp
{
    public class WebSocketClientCollection
    {
        private static List<WebSocketClient> _client = new List<WebSocketClient>();

        public static void AddClient(WebSocketClient client)
        {
            var clientNow = _client.Where(c => c.Uid == client.Uid).FirstOrDefault();
            if (clientNow != null)
                _client.Remove(client);
            _client.Add(client);
        }

        public static void RemoveClient(string clientId)
        {
            var clientNow = _client.Where(c => c.Uid == clientId).FirstOrDefault();
            if (clientNow != null)
                _client.Remove(clientNow);
        }

        public static WebSocketClient GetCLient(string clientId)
        {
            return _client.FirstOrDefault(c => c.Uid == clientId);
        }
        public static List<WebSocketClient> GetAllClient()
        {
            return _client;
        }
        //public static List<WebSocketClient> GetAllGroupClient(List<int> groupIds)
        //{
        //    var clientsNow = _client.Where(c =>c.ro);
        //    return clientsNow;
        //}

    }

}

最后附上测试效果

  到此先告一个段落

 

橙子主题打折出售

其实我不卖,主要是这里是放广告的,所以就放了一个
毕竟主题都没做完,卖了也是坑.

购买它
本文为原创文章,请注意保留出处!

热门文章

修复群晖Synology Drive client右键菜单缺失问题 本教程主要解决windows10右键菜单中没有SynologyDrive菜单的问题,整体思路是找到...修复群晖SynologyDriveclient右键菜单缺失问题 作者:Pastore Antonio
1980 浏览量
docker如何查看一个镜像内部的目录结构及其内部都有哪些文件 前言:有时候我们会在docker上下载一个镜像,或者是上传一个镜像到docker上,甚至有时候就是在...docker如何查看一个镜像内部的目录结构及其内部都有哪些文件 作者:Pastore Antonio
1922 浏览量
Adobe Acrobat Pro 激活 这里记录了一些AdobeAcrobat的激活教程和组件。浏览量:1,806 作者:Pastore Antonio
1649 浏览量
追寻日出,找回自己 为什么我要去追寻日出?其实我是一个很懒的人,每次都起不来,直到有一次我在租房中睡到了大天亮,阳光照...追寻日出,找回自己 作者:Pastore Antonio
1606 浏览量
Swagger2 接口多级分组方法 swagger无疑是Java开发的最佳伴侣,接口非常方便调试;当然也有用Postman,因人而异吧...Swagger2接口多级分组方法 作者:Pastore Antonio
1522 浏览量