WebSocket开发(记录落地)功能

前言

在上文:WebSocket开发(一对一聊天) 完成了一对一聊天的功能,但是消息补偿的功能并没有验证,这需要将客户端id的设置参数进行修改。

而且光日志打印记录WebSocket事件的流转有点不靠谱,所以需要将事件进行落地,结构化数据像用户登陆记录用户代收消息用户在线状态操作日志等业务线强的数据可以放到mysql中,像聊天记录图片漫游等已经落地的消息数据可以放到mongodb、es中备份存储。这里demo为了方便就都使用mysql存储。

在这里插入图片描述

1. 持久化设计

1.1 引入持久层框架

引入持久层框架,这里使用mybatis-plus

添加依赖

        <!-- mybatis-plus 所需依赖  -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!-- MySQL连接 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

配置yml文件

server:
  port: 5822
mybatis-plus:
  global-config:
    db-config:
      id-type: auto
      field-strategy: not_empty
      column-underline: true
      logic-delete-value: 0
      logic-not-delete-value: 1
      db-type: mysql
    refresh: false
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1/chatroom-im?useUnicode=true&characterEncoding=utf8
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: xxxx

启动类配置

启动类注解指定mapper包地址

@MapperScan("com.an.im.mapper")

1.2 表设计

因为目前没有增加用户落地的概念先不加用户表了,只按客户端定义的id为客户端用户标识,统计一下目前的流程中需要增加那些表。

  • 用户连接记录表
    • 描述:客户端建立/断开连接的日志记录表;
    • 作用:追溯数据使用
  • 客户端发送消息表
    • 描述:客户端发送的消息记录表;
    • 作用:追溯跟对照数据使用
  • 服务端发送消息表
    • 描述:服务端发送的消息记录表;
    • 作用:追溯跟对照数据使用
  • 一对一消息记录表
    • 描述:客户端发送的消息记录表;
    • 作用:双客户端聊天数据记录漫游
  • 消息补偿表
    • 描述:客户端待接收的消息记录表
    • 作用:客户端连接补偿消息使用
  • 异常记录表
    • 描述:产生异常的日志收集表
    • 作用:排除异常情况使用
1.2.1 用户连接记录表

此表主要统计用户连接跟断连的日志,核心字段就是用户id时间事件类型(连接/断连)

CREATE TABLE `chatroom-im`.`USER_LOGIN_EVENT`  (
  `id` int(0) NOT NULL,
  `uid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户id',
  `event_type` tinyint(1) NOT NULL COMMENT '0:连接;1:断连',
  `trigger_date` datetime(0) NOT NULL COMMENT '事件触发时间',
  `create_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建日期',
  `update_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.2.2 客户端发送消息表

客户端发送的所有消息都要记录下来,这一步可以异步操作,作为消息的落地存储,核心字段为客户端id时间消息内容

CREATE TABLE `chatroom-im`.`client_send_msg`  (
  `id` int(0) NOT NULL,
  `uid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端id',
  `info_msg` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '信息明细',
  `send_date` datetime(0) NOT NULL COMMENT '发送时间',
  `create_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建日期',
  `update_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改日期',
  `del_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除;1:已删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.2.2 服务端发送消息表

服务端发送消息存储的信息跟客户端的类似,将存储的客户端id修改为接收端id就可以复用

CREATE TABLE `chatroom-im`.`server_send_msg`  (
  `id` int(0) NOT NULL,
  `accept_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '接收端id',
  `info_msg` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '信息明细',
  `send_date` datetime(0) NOT NULL COMMENT '发送时间',
  `create_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建日期',
  `update_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改日期',
  `del_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除;1:已删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.2.3 一对一消息记录表

明确已经双端接收的消息进行记录作为漫游使用,核心字段:发送端id接收端id发送消息明细id接收消息明细id消息内容发送时间接收时间

CREATE TABLE `chatroom-im`.`Untitled`  (
  `id` int(0) NOT NULL,
  `send_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '发送端id',
  `accept_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '接收端id',
  `send_msg_id` int(0) NULL DEFAULT NULL COMMENT '发送消息明细id',
  `accept_msg_id` int(0) NULL DEFAULT NULL COMMENT '接收消息明细id',
  `info_msg` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '消息内容',
  `msg_send_date` datetime(0) NULL DEFAULT NULL COMMENT '消息发送时间',
  `accept_date` datetime(0) NULL DEFAULT NULL COMMENT '消息接收时间',
  `create_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建日期',
  `update_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改日期',
  `del_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除;1:已删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.2.4 消息补偿表

这个表主要记录需要补充的消息记录来做消息补偿历史数据追溯。核心字段:接收端id消息内容补偿时间补偿状态

CREATE TABLE `chatroom-im`.`client_compensate_msg`  (
  `id` int(0) NOT NULL,
  `accept_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '接收端id',
  `info_msg` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '信息明细',
  `send_date` datetime(0) NOT NULL COMMENT '发送时间',
	`compensate_satus` tinyint(1) NOT NULL COMMENT '补偿状态(0:未补偿;1:已补偿;2:失败)',
  `create_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建日期',
  `update_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改日期',
  `del_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除;1:已删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.2.5 异常记录表

异常记录主要作为在OnError事件中发生异常内容的记录,核心字段:客户端id异常内容触发事件

CREATE TABLE `chatroom-im`.`error_event_msg`  (
  `id` int(0) NOT NULL,
  `uid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '接收端id',
  `error_msg` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '异常明细',
  `trigger_date` datetime(0) NOT NULL COMMENT '触发时间',
  `create_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建日期',
  `update_date` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改日期',
  `del_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除;1:已删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

2. 事件持久化

表结构既然定义好了,就在各事件触发时进行持久化操作,需要先对这些表建立Mybatis-plus的实体跟Maaper类,这里不写出来了,后面会给出git地址

WebSocketserver里注入对应会有以下问题:

问题在websocket的server文件里是无法使用@autowired注解自动注入的
原因spring容器管理的是单例的,他只会注入一次,而websocket是多对象的,当有新的用户使用的时候,他就会新创建一个websocket对象,这就导致了用户创建的websocket对象都不能注入对象了,所以在运行的时候就会发生注入对象为null的情况;
解决方法把需要注入的service声明为静态对象,如下代码:

    private static BaseWebSocketService baseWebSocketService;

    @Autowired
    public void setService(BaseWebSocketService baseWebSocketService){
        WebSocketClient.baseWebSocketService = baseWebSocketService;
    }

这里的持久化操作我使用一个统一的接口BaseWebSocketService异步来进行处理,不会影响主业务并且方便以后可以调整是否持久化

2.1 用户连接记录持久化

用户记录的持久化是在OnOpen事件中进行的添加,代码如下:

    @OnOpen
    public void onOpen(Session session,@PathParam("clientId") String clientId){
        if (!webSocketClientMap.containsKey(clientId)){
            onlineUsers.addAndGet(1);
        }
        webSocketClientMap.put(clientId,this);
        infoSession = session;
        log.info("客户端:{}建立连接,当前在线人数:{}",clientId,onlineUsers.get());
        /**
         * 持久化
         */
        baseWebSocketService.saveUserLoginEvent(clientId,(byte) 0,new Date());
        /**
         * 消息补偿
         */
        if (!CollectionUtils.isEmpty(this.ToBeSentMap.get(clientId))){
            this.ToBeSentMap.get(clientId).forEach(userMessageModel->{
                this.sendMessage(BaseResponseMessage.success(userMessageModel));
            });
        }
    }
  • 持久化操作:saveUserLoginEvent
  • 核心字段:用户id事件类型(连接/断连)时间

user_login_event 表内数据验证:

在这里插入图片描述

2.2 客户端发送记录持久化

客户端发送信息服务端是在onMessage这个事件中接收的,因此持久化操作也在这个方法里实现,这个持久化是做信息记录的,所以只要是发送上来的数据都进行记录,将它放到方法的最前面。

代码如下:

	@OnMessage
    public void onMessage(String message, Session session,@PathParam("clientId") String clientId){
        /**
         * 持久化
         */
        baseWebSocketService.saveClientSendMsg(clientId,message,new Date());
        /**
         * 处理消息
         */
        UserMessageModel userMessageModel = JSONObject.parseObject(message, UserMessageModel.class);
        if (userMessageModel == null){
            this.sendMessage(BaseResponseMessage.error(null,"传递参数结构异常"));
        }
        if(!webSocketClientMap.containsKey(userMessageModel.getAcceptId())){
            // 放到待发送列表里
            if(!this.ToBeSentMap.containsKey(userMessageModel.getAcceptId())){
                this.ToBeSentMap.put(userMessageModel.getAcceptId(),new CopyOnWriteArrayList<>());
            }
            List<UserMessageModel> addList = this.ToBeSentMap.get(userMessageModel.getAcceptId());
            addList.add(userMessageModel);
            log.info("客户端:{} 发送消息到接受端:{} 不在线,放置到代发送列表,当前待发送列表:{}条",clientId,userMessageModel.getAcceptId(), addList.size());
            this.sendMessage(BaseResponseMessage.error(null,"接收端不在线"));
        }else{
            log.info("客户端:{} 发送到客户端:{},消息内容:{}",clientId,userMessageModel.getAcceptId(),userMessageModel.getMessage());
            webSocketClientMap.get(userMessageModel.getAcceptId()).sendMessage(BaseResponseMessage.success(userMessageModel));
            this.sendMessage(BaseResponseMessage.success(userMessageModel));
        }
    }
  • 持久化操作:saveClientSendMsg
  • 核心字段:客户端id时间消息内容

client_send_msg 表内数据验证:

在这里插入图片描述

2.3 服务端发送记录持久化

服务端发送消息是需要找到对应客户端的Session进行send事件的,我们之前创建了一个方法sendMessage专门用来做发送消息使用,所以将持久化的操作放到这里来。

但是在这里发现传参没有加客户端id,但是每次发送数据都传参客户端id并不太方便也不好维护,所以定义一个类的局部变量,在建立连接时将客户端id放到这个局部变量中

伪代码:

    private String clientId;
    
    @OnOpen
    public void onOpen(Session session,@PathParam("clientId") String clientId){
		this.clientId = clientId;
	}

这样可以直接在sendMessage方法中拿到所属的客户端id

代码如下:

    private void  sendMessage(Object message){
        try {
            baseWebSocketService.saveServerSendMsg(message,this.clientId,new Date());
            this.infoSession.getBasicRemote().sendObject(message);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EncodeException e) {
            throw new RuntimeException(e);
        }
    }
  • 持久化操作:saveServerSendMsg
  • 核心字段:接收端id时间消息内容

server_send_msg 表内数据验证:

在这里插入图片描述

2.4 一对一消息记录持久化

一对一的记录需要摘选发送端id接收端id消息内容发送时间接受时间等,处理逻辑相较复杂写,这里不考虑数据一致性,否则还有很多事情需要做,只是建立基础的信息记录。

创建一个新的方法来拆分需要记录的参数和异步记录,代码如下:

    private void toCSucceed(UserMessageModel userMessageModel){
        WebSocketClient webSocketClient = webSocketClientMap.get(userMessageModel.getAcceptId());
        BaseResponseMessage infoMsg = BaseResponseMessage.success(userMessageModel);
        /**
         * 持久化
         */
        baseWebSocketService.saveCTOCMsg(this.clientId,webSocketClient.clientId,JSONObject.toJSONString(infoMsg),new Date(),new Date());
        /**
         * 发送消息
         */
        webSocketClient.sendMessage(infoMsg);
        this.sendMessage(infoMsg);
        log.info("客户端:{} 发送到客户端:{},消息内容:{}",clientId,userMessageModel.getAcceptId(),userMessageModel.getMessage());
    }
  • 持久化操作:saveCTOCMsg
  • 核心字段:发送端id接收端id消息内容发送时间接收时间 (因为是异步操作就不计消息明细的对应id了)

c_to_c_msg 表内数据验证:

在这里插入图片描述

2.5 消息补偿记录持久化

这个也是一个比较核心的功能,记录肯定要补偿的,这一部分可以替换ConcurrentHashMap<String,List<UserMessageModel>>直接存到Mysql中,每次连接去mysql读取有没有需要补偿的记录。

2.5.1 写入消息补偿记录

之前是在服务端接受信息OnMessage事件中如果接收端不在线就放入补偿列表里,现在直接将这步调整为写入mysql消息补偿表

代码如下:

    @OnMessage
    public void onMessage(String message, Session session,@PathParam("clientId") String clientId){
        /**
         * 持久化
         */
        baseWebSocketService.saveClientSendMsg(clientId,message,new Date());
        /**
         * 处理消息
         */
        UserMessageModel userMessageModel = JSONObject.parseObject(message, UserMessageModel.class);
        if (userMessageModel == null){
            this.sendMessage(BaseResponseMessage.error(null,"传递参数结构异常"));
        }
        if(!webSocketClientMap.containsKey(userMessageModel.getAcceptId())){
            // 放到待发送列表里
            /*if(!this.ToBeSentMap.containsKey(userMessageModel.getAcceptId())){
                this.ToBeSentMap.put(userMessageModel.getAcceptId(),new CopyOnWriteArrayList<>());
            }
            List<UserMessageModel> addList = this.ToBeSentMap.get(userMessageModel.getAcceptId());
            addList.add(userMessageModel);*/
            baseWebSocketService.saveClientCompensateMsg(userMessageModel.getAcceptId(),message,(byte) 0);
            log.info("客户端:{} 发送消息到接受端:{} 不在线,放置到代发送列表,当前待发送列表:{}条",clientId,userMessageModel.getAcceptId());
            this.sendMessage(BaseResponseMessage.error(null,"接收端不在线"));
        }else{
            this.toCSucceed(userMessageModel);
        }
    }
  • 持久化操作:saveClientCompensateMsg
  • 核心字段:接收端id消息内容补偿状态

client_compensate_msg 表内数据验证:
在这里插入图片描述

2.5.2 消息补偿

消息补偿是在OnOpen事件中进行的,不再通过内存中的Map结构进行补偿,改为根据客户端id查看mysql中有没有需要补偿的数据。

代码如下:

    @OnOpen
    public void onOpen(Session session,@PathParam("clientId") String clientId){
        if (!webSocketClientMap.containsKey(clientId)){
            onlineUsers.addAndGet(1);
        }
        this.clientId = clientId;
        webSocketClientMap.put(clientId,this);
        infoSession = session;
        log.info("客户端:{}建立连接,当前在线人数:{}",clientId,onlineUsers.get());
        /**
         * 持久化
         */
        baseWebSocketService.saveUserLoginEvent(clientId,(byte) 0,new Date());
        /**
         * 消息补偿
         */
        /*
        if (!CollectionUtils.isEmpty(this.ToBeSentMap.get(clientId))){
            this.ToBeSentMap.get(clientId).forEach(userMessageModel->{
                this.sendMessage(BaseResponseMessage.success(userMessageModel));
            });
        }
        */
        List<ClientCompensateMsg> list = baseWebSocketService.queryClientCompensateMsg(clientId,0);
        if (!CollectionUtils.isEmpty(list)){
            list.forEach(userMessageModel->{
            	log.info("消息补偿记录,客户端:{},消息内容:{}",clientId,userMessageModel);
                this.sendMessage(BaseResponseMessage.success(userMessageModel));
            });
        }
    }
  • 获取补偿列表:queryClientCompensateMsg
  • 核心字段条件:接收端id未补偿状态

验证:

补偿表中有一条110ID的客户端有代发送记录,将前端的uid参数设置由时间戳改为110

var uid = 110;

重启服务进行连接验证

日志验证:

在这里插入图片描述
web验证:

在这里插入图片描述
补偿成功后将补偿表对应数据状态进行修改

2.6 异常记录持久化

这个操作比较简单,只要触发onError事件就将信息存储起来即可

代码如下:

    @OnError
    public void onError(Session session, Throwable error, @PathParam("clientId") String clientId){
        log.error("连接异常:{}",error.getMessage());
        baseWebSocketService.saveErrorEventMsg(clientId,error.getMessage());
    }
  • 持久化操作:saveErrorEventMsg
  • 核心字段:客户端id异常内容

验证:

模拟一个WebSocket的异常,随便抛出一个异常即可,只要触发onError事件即可

在这里插入图片描述
数据库记录:

在这里插入图片描述


到此完成了基础的数据落地,以上代码细节没有经过推敲所以并不太完善,大概模拟了一个落地的场景,实际记录落地还要考虑各样的 数据一致性丢数据补数据异常状态场景机制重试等等功能,所以理解这个想法即可。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
使用 .NET Core 可以很方便地实现 WebSocket 通信,以下是一个简单的示例: 1. 创建 WebSocket 控制器 在 .NET Core 中,可以通过继承 `Microsoft.AspNetCore.Mvc.ControllerBase` 来创建控制器。我们可以创建一个 `WebSocketController`,代码如下: ```csharp using Microsoft.AspNetCore.Mvc; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; namespace YourNamespace.Controllers { [ApiController] [Route("[controller]")] public class WebSocketController : ControllerBase { private readonly WebSocketManager _webSocketManager; public WebSocketController(WebSocketManager webSocketManager) { _webSocketManager = webSocketManager; } [HttpGet("{id}")] public async Task Get(string id) { if (HttpContext.WebSockets.IsWebSocketRequest) { var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); _webSocketManager.AddSocket(id, webSocket); await Receive(webSocket, async (result, buffer) => { if (result.MessageType == WebSocketMessageType.Text) { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); await _webSocketManager.SendMessageAsync(id, message); } else if (result.MessageType == WebSocketMessageType.Close) { await _webSocketManager.RemoveSocketAsync(id); } }); } else { HttpContext.Response.StatusCode = 400; } } private async Task Receive(WebSocket webSocket, Action<WebSocketReceiveResult, byte[]> handleMessage) { var buffer = new byte[1024 * 4]; while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); handleMessage(result, buffer); } } } } ``` 在上面的代码中,我们首先判断 HTTP 请求是否是 WebSocket 请求,如果是,则接受 WebSocket 连接,并将 WebSocket 加入到 `WebSocketManager` 中。然后,我们使用 `Receive` 方法处理接收到的消息,并使用 `SendMessageAsync` 方法发送消息给客户端。如果收到关闭消息,我们将 WebSocket 从 `WebSocketManager` 中移除。 2. 创建 WebSocket 管理器 我们需要一个 WebSocket 管理器来管理所有的 WebSocket 连接。我们可以创建一个 `WebSocketManager` 类,代码如下: ```csharp using System.Collections.Concurrent; using System.Net.WebSockets; using System.Text; using System.Threading.Tasks; namespace YourNamespace { public class WebSocketManager { private readonly ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>(); public void AddSocket(string id, WebSocket socket) { _sockets.TryAdd(id, socket); } public async Task SendMessageAsync(string id, string message) { if (_sockets.TryGetValue(id, out WebSocket socket)) { var bytes = Encoding.UTF8.GetBytes(message); await socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None); } } public async Task RemoveSocketAsync(string id) { _sockets.TryRemove(id, out WebSocket socket); await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by the WebSocketManager", CancellationToken.None); } } } ``` 在上面的代码中,我们使用 `ConcurrentDictionary` 来存储所有的 WebSocket 连接。我们提供了 `AddSocket` 方法来添加 WebSocket 连接,`SendMessageAsync` 方法来发送消息给指定的 WebSocket 连接,`RemoveSocketAsync` 方法来移除指定的 WebSocket 连接。 3. 注册 WebSocket 控制器和 WebSocket 管理器 最后,我们需要在 `Startup.cs` 中注册 WebSocket 控制器和 WebSocket 管理器,代码如下: ```csharp using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; namespace YourNamespace { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<WebSocketManager>(); services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } } ``` 在上面的代码中,我们首先注册 `WebSocketManager` 类型的单例依赖,然后使用 `AddControllers` 方法来注册控制器。最后,我们使用 `MapControllers` 方法将控制器映射到路由。 现在,我们可以使用浏览器或其他 WebSocket 客户端连接到 `/WebSocket/{id}` 路径,并发送和接收消息了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余生大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值