上一次我已经讲了在webapi主机上面加入websocket中间件。

这次就更进一步,搭建一个websocket局域网聊天室。

传送门-->webapi添加添加websocket中间件

下一篇 - 基于webapi的websocket聊天室(二)


聊天室

websocket通信其实和win32api里面的消息循环差不多,只不过一个消息来自操作系统,一个来自网络。

但核心都是一个阻塞的while循环,在循环中处理各种消息。

由于搭建聊天室代码多一点,我就把中间件单独写一个类,而不是用lambadu委托在program里面直接写了。

  • 聊天室成员

    游客对象可以是单纯的WebSocket对象,也可以是更复杂的对象。但我们还需要为游客命名等,所以采用自定义一个游客类来代表游客信息。
//RoomVisitor.cs

public class RoomVisitor
{
/// <summary>
/// 网络连接
/// </summary>
public WebSocket Web { get; set; }
/// <summary>
/// 名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// ID
/// </summary>
public string Id { get; set; }
}

我暂时就考虑这些字段。其实还有,比如前面已经添加了身份认证的中间件,可以取得其他信息。但是目前为了简单,先不考虑认证的问题。不考虑会员,只考虑游客。

  • 聊天室对象

    在初始阶段。我们先不考虑从那么复杂,不考虑多少个聊天室的情况,就考虑单聊天室。这样我们就可以使用单例模式进行依赖注入。
/// <summary>
/// 聊天室
/// </summary>
public class WebSocketChatRoom
{
/// <summary>
/// 成员
/// </summary>
public ConcurrentDictionary<string, RoomVisitor> clients=new ConcurrentDictionary<string, RoomVisitor>(); public WebSocketChatRoom()
{ }
}
//program.cs

//添加聊天室服务
builder.Services.AddSingleton<WebSocketChatRoom>();

聊天室功能

为了不那么单调,我们只定义聊天室三个最简单的功能。

  • 上线通知
  • 发言广播
  • 下线通知

消息循环条件

在win32api中消息循环是while(有消息),直到接收到窗体关闭消息,退出循环,程序未关闭。

websocket类似。需要while(client.CloseStatus.HasValue==false),连接未关闭时一直循环。

当然,这是个阻塞式循环

上线通知和下线通知

这两个功能倒是简单。在websocket连接建立时和连接关闭时广播一条消息就行。

也就是在进入消息循环前退出消息循环后

//游客加入聊天室
var visitor = new RoomVisitor() { Id= System.Guid.NewGuid().ToString("N"), Name = $"游客_{clients.Count + 1}", Web = client };
while(client.CloseStatus.HasValue==false)
{
//...
}
//广播游客退出
CascadeMeaasge(visitor,$"{visitor.Name}退出聊天室");
clients.TryRemove(visitor.Id, out RoomVisitor v);
//关闭连接...
await client.CloseAsync(
client.CloseStatus!.Value,
client.CloseStatusDescription,
CancellationToken.None);

发言广播

在收到消息时,遍历聊天室成员,依次转发这条消息。

当然,还可以加一些处理。比如添加发言人名字,时间等。

  • 首先要定义广播方法,CascadeMeaasge
public void CascadeMeaasge(RoomVisitor visitor, string message)
{
foreach (var other in clients)
{
//不对发言者广播
if (visitor!=null)
{
if (other.Key == visitor.Id)
{
continue;
}
}
//转发内容
var buffer = Encoding.UTF8.GetBytes(message);
if (other.Value.Web.State==WebSocketState.Open)
{
other.Value.Web.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}
  • 收到消息时广播转发
//消息缓冲区。每个连接分配400字节,100个汉字的内存
var minimumBuffer=new byte[400];
while(!client.CloseStatus.HasValue)
{ WebSocketReceiveResult result = await client.ReceiveAsync(new ArraySegment<byte>(minimumBuffer), CancellationToken.None);
//广播游客发言
if (result.MessageType == WebSocketMessageType.Text)
{
CascadeMeaasge(visitor, $"{visitor.Name}: "+UTF8Encoding.UTF8.GetString(minimumBuffer,0, result.Count));
}
}

聊天室的核心功能就完成了,实在是没几行代码。

添加中间件处理websocket连接

websocket连接很多,不一定都是要进入聊天室。我们使用/chat路径来路由到聊天室。

  • 中间件定义
//WebSocketChatRoomMiddleware.cs

public class WebSocketChatRoomMiddleware
{
private readonly RequestDelegate _next; public WebSocketChatRoomMiddleware(RequestDelegate next, Func<WebSocketChatRoom> handler)
{
_next = next;
Handler = handler;
} public Func<WebSocketChatRoom> Handler { get; } public async Task InvokeAsync(HttpContext context)
{
await _next(context);
if (context.Request.Path=="/chat")
{
WebSocket client = await context.WebSockets.AcceptWebSocketAsync();
await Handler().HandleContext(context, client);
} }
}

我们使用MapWhen单独给websocket连接做一个管道分支,区别于http处理流程。只有websocket连接才会进入这个分支。

注意授权中间件,如果没做,可以删掉。

//WebSocketChatRoomMiddleware.cs

public static class WebSocketChatRoomMiddlewareExtensions
{
public static WebApplication UseWebSocketChatRoomMiddleware(this WebApplication builder)
{
//建立websocket分支
builder.MapWhen(c => c.WebSockets.IsWebSocketRequest, appbuilder =>
{
//授权
appbuilder.Use(async (context, next) =>
{
if (context.User.Identity.IsAuthenticated)
await next(context);
else
context.Response.StatusCode = StatusCodes.Status403Forbidden;
})
//连接
.UseMiddleware<WebSocketChatRoomMiddleware>(new Func<WebSocketChatRoom>(() =>
{
return appbuilder.ApplicationServices.GetRequiredService<WebSocketChatRoom>();
}));
});
return builder;
}
}
  • 添加中间件
//program.cs

//调用了此终结点才能判断连接请求是不是ws请求,会让ws连接的context.WebSockets.IsWebSocketRequest变成true
app.UseWebSockets();
//使用我们定义的中间件
app.UseWebSocketChatRoomMiddleware();

聊天测试

客户端就直接用api请求工具吧,懒得写了。我使用了ApiPost来测试。

我定义了三个游客来测试广播功能

  • 游客定义

  • 测试步骤

  1. 游客1、游客2、游客3依次连接聊天室。查看加入聊天室的广播信息。
  2. 游客1、游客2、游客3依次发言。查看发言广播。
  3. 游客1、游客2、游客3依次退出聊天室。查看退出聊天室广播信息。

基于webapi的websocket聊天室(一)的更多相关文章

  1. 基于springboot的websocket聊天室

    WebSocket入门 1.概述 1.1 Http #http简介 HTTP是一个应用层协议,无状态的,端口号为80.主要的版本有1.0/1.1/2.0. #http1.0/1.1/2.0 1.HTT ...

  2. 使用.NET Core和Vue搭建WebSocket聊天室

    博客地址是:https://qinyuanpei.github.io.  WebSocket是HTML5标准中的一部分,从Socket这个字眼我们就可以知道,这是一种网络通信协议.WebSocket是 ...

  3. 基于flask的网页聊天室(四)

    基于flask的网页聊天室(四) 前言 接前天的内容,今天完成了消息的处理 具体内容 上次使用了flask_login做用户登录,但是直接访问login_requare装饰的函数会报401错误,这里可 ...

  4. websocket聊天室

    目录 websocket方法总结 群聊功能 基于websocket聊天室(版本一) websocket方法总结 # 后端 3个 class ChatConsumer(WebsocketConsumer ...

  5. WebSocket聊天室demo

    根据Socket异步聊天室修改成WebSocket聊天室 WebSocket特别的地方是 握手和消息内容的编码.解码(添加了ServerHelper协助处理) ServerHelper: using ...

  6. Netty入门(一)之webSocket聊天室

    一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...

  7. 基于flask的网页聊天室(三)

    基于flask的网页聊天室(三) 前言 继续上一次的内容,今天完成了csrf防御的添加,用户头像的存储以及用户的登录状态 具体内容 首先是添加csrf的防御,为整个app添加防御: from flas ...

  8. 基于flask的网页聊天室(二)

    基于flask的网页聊天室(二) 前言 接上一次的内容继续完善,今天完成的内容不是很多,只是简单的用户注册登录,内容具体如下 具体内容 这次要加入与数据哭交互的操作,所以首先要建立相关表结构,这里使用 ...

  9. 基于flask的网页聊天室(一)

    基于flask的网页聊天室(一) 基本目标 基于flask实现的web聊天室,具有基本的登录注册,多人发送消息,接受消息 扩展目标 除基本目标外添加当前在线人数,消息回复,markdown支持,历史消 ...

  10. 用Java构建一个简单的WebSocket聊天室

    前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...

随机推荐

  1. OpenHarmony创新赛 | 赛事宣讲会日期重磅官宣!为你带来超详尽的赛事攻略

      最近接到很多小伙伴私信提问,比如如何报名开放原子开源大赛OpenAtom OpenHarmony(简称:"OpenHarmony")创新赛?有哪些赛题可以报名参赛?现在,赛事宣 ...

  2. DOM 节点遍历:掌握遍历 XML文档结构和内容的技巧

    遍历是指通过或遍历节点树 遍历节点树 通常,您想要循环一个 XML 文档,例如:当您想要提取每个元素的值时. 这被称为"遍历节点树". 下面的示例循环遍历所有 <book&g ...

  3. 深入了解 Spring Cloud Config、Spring Cloud Gateway 与断路器模式

    Spring Microservices 是一个框架,它使用 Spring 框架更容易地构建和管理基于微服务的应用程序.微服务是一种架构风格,其中一个大型应用程序被构建为一组小型.独立可部署的服务.每 ...

  4. C#利用自动化接口编写OPC客户端,OPC Client,源码直接放网盘

    引用:https://www.cnblogs.com/flh1/p/12409266.html 链接: https://pan.baidu.com/s/1Vs08c7qjShEc9GQ8dvCkdg ...

  5. 初学STM32 SDIO(一)

    1. SDIO协议简介 ​ SDIO全称是安全数字输入/输出接口 ,控制器对SD卡进行读写通信操作一般有两种通信接口可选,一种是SPI接口,另外一种就是SDIO接口. 多媒体卡(MMC).SD卡. S ...

  6. stack-c++

    #include <iostream> using namespace std; template <typename T> class Stack{ private: int ...

  7. 重新整理 .net core 实践篇—————Entity的定义[二十五]

    前言 简单介绍一下实体模型的设计. 正文 前文提及了我们的应用分为: 共享层 基础设施层 领域层 应用层 今天来介绍领域模型层. 前文提及到领域模型在共享层有一个领域模型抽象类库. 里面有这些类: 先 ...

  8. 重新整理数据结构与算法(c#)——算法套佛洛伊德算法[三十二]

    前言 佛洛伊德算法和迪杰斯特拉算法非常像,但是它求的是任何一个点到其他点之间的距离. 假设有一张图: 转换为矩阵为: 他们的前驱为: 可能上面表述前驱不清楚,举个例子. 看下图: 这第二种图表示,从A ...

  9. 见鬼了!我家的 WiFi 只有下雨天才能正常使用...

    这是作者大学时期在家里遇到的一个非常奇怪的网络问题,作者的父亲是一名经验丰富的网络工程师,他们家里使用了一个复杂的网络设置,通过 Wi-Fi 桥接的方式,将父亲公司的高速商业网络连接到家中.但是有一天 ...

  10. 中国大陆地区维护的Linux操作系统

    Linux开源生态丰富,中国大陆地区基于CentOS停服,依托阿里云.腾讯云.华为云三大私营企业,相继发布了自己的开源Linux定制版,很高兴的是他们只是改个名字并没有选择闭门造车,只是官网还是很不耻 ...