创建WebSocket服务器
定义
TouchSocket.HttpTouchSocket.Http.WebSocketsdotnet add package TouchSocket.Http一、说明
WebSocket服务器是基于HTTP协议升级而来的长连接通信协议服务器。它继承自HttpService,在完成HTTP握手后,通过协议升级建立WebSocket连接。每个成功连接的客户端都会在服务器端创建一个对应的HttpSessionClient实例,后续的所有WebSocket通信都通过该实例完成。
二、特点
- 基于HTTP协议升级,支持标准WebSocket协议。
- 支持文本、二进制以及其他Type数据传输。
- 内置心跳机制(Ping/Pong)。
- 支持数据帧分包和组合。
- 支持WSS(WebSocket Secure)加密连接。
- 支持多种连接验证方式。
- 高性能异步处理。
- 基于插件驱动,支持AOP编程。
三、产品应用场景
- 实时通信应用:聊天室、在线客服、实时协作等。
- 实时数据推送:股票行情、游戏数据、监控数据等。
- 物联网设备通信:传感器数据上报、设备控制等。
- Web应用实时交互:在线编辑器、实时画板等。
四、服务器架构
4.1 连接架构
WebSocket服务器基于HTTP服务器,当收到WebSocket握手请求时,会将HTTP连接升级为WebSocket连接。每个WebSocket连接对应一个HttpSessionClient实例。
4.2 协议升级流程
- 客户端发送HTTP握手请求
- 服务器验证握手请求
- 服务器响应握手成功
- 连接升级为WebSocket协议
- 开始WebSocket数据帧通信
五、可配置项
继承HttpService的所有配置项,无特殊配置。
六、支持插件接口
| 插件方法 | 功能 |
|---|---|
| IWebSocketConnectingPlugin | 当收到握手请求之前,可以进行连接验证等 |
| IWebSocketConnectedPlugin | 当成功握手响应之后 |
| IWebSocketReceivedPlugin | 当收到Websocket的数据报文 |
| IWebSocketClosingPlugin | 当收到关闭请求时触发。如果对方直接断开连接,则此方法则不会触发。 |
| IWebSocketClosedPlugin | 当WebSocket连接断开时触发,无论是否正常断开。但如果是断网等操作,可能不会立即执行,需要结合心跳操作和CheckClear插件来进行清理。 |
七、创建WebSocket服务器
7.1 简单直接创建
最简单的方式是使用WebSocket插件,直接指定URL路由来接收WebSocket连接。
7.2 验证连接
可以对连接的URL、Query参数、Header等进行验证,决定是否允许WebSocket连接。
在验证过程中,如果url不匹配,或者不包含升级协议头的话,一般不需要额外处理,直接返回false即可。随后这个请求会被当作普通的HTTP请求处理。
如果url正确,但是其他鉴权没通过时,才需要直接进行Http响应。
7.3 其他方式创建
实际上,只要在升级协议后,能访问到HttpContext,即可通过SwitchProtocolToWebSocketAsync方式创建WebSocket连接更加灵活,可以方便地获取HTTP参数,实现多个URL的连接路由。
SwitchProtocolToWebSocketAsync后的WebSocket实例还是会放置在所在的HttpSessionClient中,可以通过HttpSessionClient.WebSocket获取到。
7.4 创建基于SSL的WebSocket服务(WSS)
创建WSS(WebSocket Secure)服务器,只需在配置中添加SSL选项。详情: Http服务器配置Ssl
八、接收消息
WebSocket服务器有多种接收消息的方式,可以根据不同的使用场景选择合适的方法。
8.1 插件方式接收消息
使用插件接收消息是最推荐的方式,它提供了高度解耦和灵活的数据处理能力。
(1)定义插件:
(2)配置使用插件:
在服务器端,默认情况下插件的所有函数都可能被并发执行,因此应当做好线程安全处理。更多详情请参考:插件开发使用指南
8.2 异步阻塞接收
异步阻塞接收是通过直接调用WebSocket的ReadAsync方法来同步阻塞式读取数据。这种方式的特点是能在代码上下文中直接获取数据,便于处理复杂的数据逻辑。
一般的,在IWebSocketConnectedPlugin插件中(即刚连接成功)调用ReadAsync方法来接收数据。
ReadAsync方式是异步非阻塞的接收方式,不会占用线程资源,只会阻塞当前Task。因此可以大量使用,不需要考虑性能问题。
使用ReadAsync方式会阻塞IWebSocketConnectedPlugin的插件传递链,在收到WebSocket消息时不会触发IWebSocketReceivedPlugin插件。
8.3 接收中继数据
WebSocket在接收大数据时,可能会分包接收。可以通过WSDataFrame.Opcode的值是不是WSDataType.Cont来判断是不是分包数据。
分包数据的处理方式有很多,下面提供一种内存缓存的方式:
内存缓存的方式适合数据量不大的场景,如果数据量较大,建议使用其他缓存等方式。
九、发送数据
按照WebSocket服务器架构,每个成功连接的客户端都会在服务器端创建一个HttpSessionClient实例。要发送WebSocket消息,需要通过这些实例进行操作。
9.1 获取客户端实例
一般的,如果在插件中收到信息时,可以直接拿到HttpSessionClient实例。或者IWebSocket对象。此时直接操作即可。
如果在服务器的其他地方想要发送消息,可以通过下面代码获取所有在线客户端,然后选择需要发送的客户端。
9.2 发送文本消息
9.3 发送二进制消息
9.4 发送自定义数据帧
9.5 发送Ping、Pong消息
9.7 发送大数据(分包)
发送大数据时,需要分包发送,可以使用SendAsync的重载方法,设置FIN标志。
十、连接管理
10.1 主动关闭连接
在使用WebSocket时,如果想主动关闭连接,可以使用CloseAsync方法,同时可以携带一个关闭原因。
默认关闭状态码为1000。意为:正常关闭。