跳到主要内容
版本:4.1

创建WebSocket服务器

定义

命名空间:
TouchSocket.HttpTouchSocket.Http.WebSockets
安装:
dotnet 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 协议升级流程

  1. 客户端发送HTTP握手请求
  2. 服务器验证握手请求
  3. 服务器响应握手成功
  4. 连接升级为WebSocket协议
  5. 开始WebSocket数据帧通信

五、可配置项

继承HttpService的所有配置项,无特殊配置。

六、支持插件接口

插件方法功能
IWebSocketConnectingPlugin当收到握手请求之前,可以进行连接验证等
IWebSocketConnectedPlugin当成功握手响应之后
IWebSocketReceivedPlugin当收到Websocket的数据报文
IWebSocketClosingPlugin当收到关闭请求时触发。如果对方直接断开连接,则此方法则不会触发。
IWebSocketClosedPlugin当WebSocket连接断开时触发,无论是否正常断开。但如果是断网等操作,可能不会立即执行,需要结合心跳操作和CheckClear插件来进行清理。

七、创建WebSocket服务器

7.1 简单直接创建

最简单的方式是使用WebSocket插件,直接指定URL路由来接收WebSocket连接。

🔄 正在加载代码...

7.2 验证连接

可以对连接的URLQuery参数、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 异步读取(ReadAsync)

ReadAsyncIWebSocket提供的异步读取方法,允许以顺序化的代码风格轮询等待并处理数据,非常适合需要处理复杂数据逻辑、有状态协议或请求-响应模式的场景。

信息

ReadAsync异步非阻塞的接收方式,不会占用线程资源,只会挂起当前Task直到数据到达。因此可以大量使用,无需担心性能问题。

注意

使用ReadAsync方式时,框架不会触发IWebSocketReceivedPlugin插件中的接收事件。两种接收方式不能同时混用。

在服务器端使用ReadAsync时,有以下两个关键要点:

  1. 必须将autoReceive设为false:调用SwitchProtocolToWebSocketAsync(false)时,false参数表示关闭框架的自动接收循环,从而由开发者通过ReadAsync主动拉取数据。若不设置,框架会自动消费数据,ReadAsync将永远收不到消息。
  2. 必须在新的异步任务中执行读取循环:不能在调用SwitchProtocolToWebSocketAsync的当前任务线程中直接执行读取,必须使用EasyTask.SafeNewRun启动新任务,因为只有当升级函数执行完毕并return后,WebSocket升级才算完成。
🔄 正在加载代码...

8.3 使用ReadStringAsync和ReadBinaryAsync

框架提供了ReadStringAsyncReadBinaryAsync两个扩展方法,是对ReadAsync的高级封装,能够自动处理分包、拼包,无需手动判断WSDataType.Cont中继包,让代码更加简洁:

  • ReadStringAsync:自动合并所有文本类型的数据帧,直接返回完整字符串。
  • ReadBinaryAsync:自动合并所有二进制类型的数据帧,将完整数据写入ByteBlockStream
🔄 正在加载代码...
🔄 正在加载代码...
提示

ReadStringAsyncReadBinaryAsync在收到非预期类型的数据帧时会抛出异常,请确保通信双方的数据类型约定一致。

8.4 接收中继数据

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。意为:正常关闭。

🔄 正在加载代码...

十一、示例Demo