一、说明

WebSocket是基于Http协议的升级协议,所以应当挂载在http服务器执行。

二、可配置项

继承HttpService

三、支持插件接口

支持ITcpPlugin、IHttpPlugin、IWebSocketPlugin

|

IWebSocketPlugin

OnHandshaking 表示在即将握手连接时。
OnHandshaked 表示完成握手后。
OnHandleWSDataFrame 当收到WS数据时。


四、创建WebSocket服务

4.1 简单通过插件创建

通过插件创建的话,只能指定一个特殊url路由。如果想获得连接前的Http请求,也必须再添加一个实现IWebSocketPlugin接口的插件,然后从OnHandshaking方法中捕获。

  1. var service = new HttpService();
  2. service.Setup(new TouchSocketConfig()//加载配置
  3. .UsePlugin()
  4. .SetListenIPHosts(new IPHost[] { new IPHost(7789) })
  5. .ConfigureContainer(a =>
  6. {
  7. a.SetSingletonLogger<ConsoleLogger>();
  8. })
  9. .ConfigurePlugins(a =>
  10. {
  11. a.Add<WebSocketServerPlugin>()//添加WebSocket功能
  12. .SetWSUrl("/ws")
  13. .SetCallback(WSCallback);//WSCallback回调函数是在WS收到数据时触发回调的。
  14. a.Add<MyWebSocketPlugin>();//MyWebSocketPlugin是继承自WebSocketPluginBase的插件。
  15. }))
  16. .Start();
  17. Console.WriteLine("Http服务器已启动");
  18. Console.WriteLine("ws://127.0.0.1:7789/ws");

4.2 通过WebApi创建

通过WebApi的方式会更加灵活,也能很方便的获得Http相关参数。还能实现多个Url的连接路由。
实现步骤:

  1. 必须启用插件
  2. 必须配置ConfigureRpcStore,和注册MyServer
  3. 必须添加WebApiParserPlugin ```csharp var service = new HttpService(); service.Setup(new TouchSocketConfig()//加载配置 .UsePlugin() .SetListenIPHosts(new IPHost[] { new IPHost(7789) }) .ConfigureContainer(a => {
    1. a.SetSingletonLogger<ConsoleLogger>();
    }) .ConfigureRpcStore(a=> {
    1. a.RegisterServer<MyServer>();
    }) .ConfigurePlugins(a => {
    1. a.Add<WebApiParserPlugin>();
    })) .Start();

Console.WriteLine(“服务器已启动,可使用下列地址连接”); Console.WriteLine(“ws://127.0.0.1:7789/MyServer/ConnectWS”);

  1. ```csharp
  2. public class MyServer : RpcServer
  3. {
  4. private readonly ILog m_logger;
  5. public MyServer(ILog logger)
  6. {
  7. this.m_logger = logger;
  8. }
  9. [Router("/[api]/[action]")]
  10. [WebApi(HttpMethodType.GET, MethodFlags = MethodFlags.IncludeCallContext)]
  11. public void ConnectWS(WebApiServerCallContext callContext)
  12. {
  13. if (callContext.Caller is HttpSocketClient socketClient)
  14. {
  15. if (socketClient.SwitchProtocolToWebSocket(callContext.Context))
  16. {
  17. m_logger.Message("WS通过WebApi连接");
  18. }
  19. }
  20. }
  21. }

创建基于Ssl的WebSocket服务

创建WSs服务器时,其他配置不变,只需要在config中配置SslOption代码即可。
RRQMBox中,放置了一个自制Ssl证书,密码为“RRQMSocket”以供测试。使用配置非常方便。

  1. var config = new TouchSocketConfig();
  2. config.UsePlugin()
  3. .SetReceiveType(ReceiveType.Auto)
  4. .SetListenIPHosts(new IPHost[] { new IPHost(7789) })
  5. .SetServiceSslOption(new ServiceSslOption() //Ssl配置,当为null的时候,相当于创建了ws服务器,当赋值的时候,相当于wss服务器。
  6. {
  7. Certificate = new X509Certificate2("RRQMSocket.pfx", "RRQMSocket"),
  8. SslProtocols = SslProtocols.Tls12
  9. });

接收消息

【回调接收】
在添加WebSocketServerPlugin插件后,可以调用SetCallback函数,然后设置一个回调函数(如下所示),然后该函数在服务器收到信息时,会触发(并发触发)。

  1. static void WSCallback(ITcpClientBase client, WSDataFrameEventArgs e)
  2. {
  3. switch (e.DataFrame.Opcode)
  4. {
  5. case WSDataType.Cont:
  6. Console.WriteLine($"收到中间数据,长度为:{e.DataFrame.PayloadLength}");
  7. break;
  8. case WSDataType.Text:
  9. Console.WriteLine(e.DataFrame.ToText());
  10. break;
  11. case WSDataType.Binary:
  12. if (e.DataFrame.FIN)
  13. {
  14. Console.WriteLine($"收到二进制数据,长度为:{e.DataFrame.PayloadLength}");
  15. }
  16. else
  17. {
  18. Console.WriteLine($"收到未结束的二进制数据,长度为:{e.DataFrame.PayloadLength}");
  19. }
  20. break;
  21. case WSDataType.Close:
  22. {
  23. Console.WriteLine("远程请求断开");
  24. client.Close("断开");
  25. }
  26. break;
  27. case WSDataType.Ping:
  28. break;
  29. case WSDataType.Pong:
  30. break;
  31. default:
  32. break;
  33. }
  34. }

【继承源插件接收】
实际上WebSocketServerPlugin是可以被继承的,然后重写OnHandleWSDataFrame函数,但尽量不要覆盖基类方法,不然插件其他将不会触发。

  1. class MyWebSocketServerPlugin: WebSocketServerPlugin
  2. {
  3. protected override void OnHandleWSDataFrame(ITcpClientBase client, WSDataFrameEventArgs e)
  4. {
  5. base.OnHandleWSDataFrame(client, e);
  6. }
  7. }

【插件接口接收】
WS服务器,虽然是Http的插件,但是也能触发插件接口。适用于WS的插件接口是IWebSocketPlugin(或者从WebSocketPluginBase继承),声明任意类,实现该接口即可。

  1. class MyWebSocketServerPlugin: WebSocketPluginBase
  2. {
  3. protected override void OnHandleWSDataFrame(ITcpClientBase client, WSDataFrameEventArgs e)
  4. {
  5. //此处的父类方法可以直接覆盖。
  6. base.OnHandleWSDataFrame(client, e);
  7. }
  8. }

回复、响应数据

在以上接收、或直接从HttpService获取Clients,将client对象转为HttpSocketClient,即可使用扩展方法,进行发送。

不要直接Send,7.x版本直接Send可以,但8.0以后,Send只会以TCP数据回应。

作为一条消息发送
image.png

服务器广播发送

  1. //广播给所有人
  2. if (client is HttpSocketClient socketClient && socketClient.Service is HttpService service)
  3. {
  4. var clients = service.GetClients();
  5. foreach (var item in clients)
  6. {
  7. item.SendWithWS(e.DataFrame.ToText());
  8. }
  9. }

将一个数据分包发送
例如:发送的数据为{0,1,2,3,4,5,6,7,8,9},当设置packageSize为5时,会先发送{0,1,2,3,4}作为头包,然后发送{5,6,7,8,9}的后继包。
image.png