在本文中,我们将详细介绍RFC 6455 WebSocket
规范,并配置一个通用的.NET 5应用程序通过WebSocket
连接与SignalR
通信。
我们将深入底层的概念,以理解底层发生了什么。
关于WebSocket
引入WebSocket
是为了实现客户端和服务器之间的双向通信。HTTP 1.0的一个痛点是每次向服务器发送请求时创建和关闭连接。但是,在HTTP 1.1
中,通过使用保持连接机制引入了持久连接(RFC 2616
)。这样,连接可以被多个请求重用——这将减少延迟,因为服务器知道客户端,它们不需要在每个请求的握手过程中启动。
WebSocket
建立在HTTP 1.1规范之上,因为它允许持久连接。因此,当你第一次创建WebSocket
连接时,它本质上是一个HTTP 1.1
请求(稍后详细介绍)。这使得客户端和服务器之间能够进行实时通信。简单地说,下图描述了在发起(握手)、数据传输和关闭WS
连接期间发生的事情。我们将在后面更深入地研究这些概念。
协议中包含了两部分:握手和数据传输。
握手
让我们先从握手开始。
简单地说,WebSocket
连接基于单个端口上的HTTP(和作为传输的TCP)。下面是这些步骤的总结。
-
服务器必须监听传入的TCP套接字连接。这可以是你分配的任何端口—通常是
80
或443
。 -
客户端通过一个HTTP GET请求发起开始握手(否则服务器将不知道与谁对话)——这是“
WebSockets
”中的“Web”部分。在消息报头中,客户端将请求服务器将连接升级到WebSocket
。 -
服务器发送一个握手响应,告诉客户端它将把协议从HTTP更改为
WebSocket
。 -
客户端和服务器双方协商连接细节。任何一方都可以退出。
下面是一个典型的打开(客户端)握手请求的样子。
GET /ws-endpoint HTTP/1.1
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: L4kHN+1Bx7zKbxsDbqgzHw==
Sec-WebSocket-Version: 13
注意客户端是如何在请求中发送Connection: Upgrade
和Upgrade: websocket
报头的。
并且,服务器握手响应。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: CTPN8jCb3BUjBjBtdjwSQCytuBo=
数据传输
我们需要理解的下一个关键概念是数据传输。任何一方都可以在任何给定的时间发送消息——因为它是一个全双工通信协议。
消息由一个或多个帧组成。帧的类型可以是文本(UTF-8
)、二进制和控制帧(例如0x8 (Close
)、0x9 (Ping
)和0xA (Pong
))。
安装
让我们付诸行动,看看它是如何工作的。
首先创建一个 ASP.NET 5 WebAPI 项目。
dotnet new webapi -n WebSocketsTutorial
dotnet new sln
dotnet sln add WebSocketsTutorial
现在添加SignalR
到项目中。
dotnet add WebSocketsTutorial/ package Microsoft.AspNet.SignalR
示例代码
我们首先将WebSockets
中间件添加到我们的WebAPI
应用程序中。打开Startup.cs
,向Configure
方法添加下面的代码。
在本教程中,我喜欢保持简单。因此,我不打算讨论SignalR
。它将完全基于WebSocket
通信。你也可以用原始的WebSockets
实现同样的功能,如果你想让事情变得更简单,你不需要使用SignalR
。
app.UseWebSockets();
接下来,我们将删除默认的WeatherForecastController
,并添加一个名为WebSocketsController
的新控制器。注意,我们将只是使用一个控制器action
,而不是拦截请求管道。
这个控制器的完整代码如下所示。
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace WebSocketsTutorial.Controllers{
[ApiController]
[Route("[controller]")]
public class WebSocketsController : ControllerBase
{
private readonly ILogger<WebSocketsController> _logger;
public WebSocketsController(ILogger<WebSocketsController> logger)
{
_logger = logger;
}
[HttpGet("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
_logger.Log(LogLevel.Information, "WebSocket connection established");
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = 400;
}
}
private async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
_logger.Log(LogLevel.Information, "Message received from Client");
while (!result.CloseStatus.HasValue)
{
var serverMsg = Encoding.UTF8.GetBytes($"Server: Hello. You said: {
Encoding.UTF8.GetString(buffer)}");
await webSocket.SendAsync(new ArraySegment<byte>(serverMsg, 0, serverMsg.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);
_logger.Log(LogLevel.Information, "Message sent to Client");
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
_logger.Log(LogLevel.Information, "Message received from Client");
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
_logger.Log(LogLevel.Information, "WebSocket connection closed");
}
}
}
这是我们所做的。
1、添加一个名为ws/
的新路由。
2、检查当前请求是否通过WebSockets
,否则抛出400
。
3、等待,直到客户端发起请求。
4、进入一个循环,直到客户端关闭连接。
5、在循环中,我们将发送“Server: Hello. You said: <client’s message>
”信息,并把它发回给客户端。
6、等待,直到客户端发送另一个请求。
注意,在初始握手之后,服务器不需要等待客户端发送请求来将消息推送到客户端。让我们运行应用程序,看看它是否工作。
dotnet run --project WebSocketsTutorial
运行应用程序后,请访问https://localhost:5001/swagger/index.html
,应该看到Swagger UI
。
现在我们将看到如何让客户端和服务器彼此通信。在这个演示中,我将使用Chrome的DevTools
(打开新标签→检查或按F12→控制台标签)。但是,你可以选择任何客户端。
首先,我们将创建一个到服务器终结点的WebSocket
连接。
let webSocket = new WebSocket('wss://localhost:5001/ws');
它所做的是,在客户端和服务器之间发起一个连接。wss://
是WebSockets
安全协议,因为我们的WebAPI
应用程序是通过TLS
服务的。
然后,可以通过调用webSocket.send()
方法发送消息。你的控制台应该类似于下面的控制台。
让我们仔细看看WebSocket
连接
如果转到Network
选项卡,则通过WS
选项卡过滤掉请求,并单击最后一个称为WS
的请求。
单击Messages
选项卡并检查来回传递的消息。在此期间,如果调用以下命令,将能够看到“This was sent from the Client!
”。试试吧!
webSocket.send("Client: Hello");
如你所见,服务器确实需要等待客户端发送响应(即在初始握手之后),并且客户端可以发送消息而不会被阻塞。这是全双工通信。我们已经讨论了WebSocket
通信的数据传输方面。作为练习,你可以运行一个循环将消息推送到客户机,以查看它的运行情况。
除此之外,服务器和客户端还可以通过ping-pong
来查看客户端是否还活着。这是WebSockets
中的一个实际特性!如果你真的想看看这些数据包,你可以使用像WireShark
这样的工具来了解。
它是如何握手的?好吧,如果你跳转到Headers
选项卡,你将能够看到我们在这篇文章的第一部分谈到的请求-响应标题。
也可以尝试一下webSocket.close()
,这样我们就可以完全覆盖open-data-close
循环了。
结论
如果你对WebSocket
的RFC
感兴趣,请访问RFC 6455
并阅读。这篇文章只是触及了WebSocket
的表面,还有很多其他的东西我们可以讨论,比如安全,负载平衡,代理等等。
.net core 中使用 websocket
感觉放了好长时间的假期。贴个 websocket
的简单示例。
整体文件结构
- 项目(WebSockets.Test)
|-- Extensions
| |-- SocketsExtension.cs
|-- Handlers
| |-- WebSocketMessageHandler.cs
|-- SocketsManager
| |-- SocketsHandler.cs
| |-- SocketsManager.cs
| |-- SocketsMiddleware.cs
|-- Program.cs
|-- Startup.cs
大体需要的文件是这些,这是最基本的示例,可以按需自行修改。
1、创建保存 WebSocket
的类
该类用于保存所有 WebSocket
。
// 文件:SocketsManager/SocketsManager.cs
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace WebSockets.Test.SocketsManager
{
public class SocketsManager
{
private readonly ConcurrentDictionary<string, WebSocket> _connections =
new ConcurrentDictionary<string, WebSocket>();
/// <summary>
/// 获取所有 sockets 的字典集合
/// </summary>
/// <returns></returns>
public ConcurrentDictionary<string, WebSocket> GetAllConnections()
{
return _connections;
}
/// <summary>
/// 获取指定 id 的 socket
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public WebSocket GetSocketById(string id)
{
return _connections.FirstOrDefault(x => x.Key == id).Value;
}
/// <summary>
/// 根据 socket 获取其 id
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public string GetId(WebSocket socket)
{
return _connections.FirstOrDefault(x => x.Value == socket).Key;
}
/// <summary>
/// 删除指定 id 的 socket,并关闭该链接
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task RemoveSocketAsync(string id)
{
_connections.TryRemove(id, out var socket);
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "socket connection closed",
CancellationToken.None);
}
/// <summary>
/// 添加一个 socket
/// </summary>
/// <param name="socket"></param>
public void AddSocket(WebSocket socket)
{
_connections.TryAdd(CreateId(), socket);
}
/// <summary>
/// 创建 id
/// </summary>
/// <returns></returns>
private string CreateId()
{
return Guid.NewGuid().ToString("N");
}
}
}
2、创建管理和操作 WebSocket
的基类
该类旨在处理 socket
的连接和断连,以及接收和发送消息,属于基类。
// 文件: SocketsManager/SocketsHandle.cs
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSockets.Test.SocketsManager
{
public abstract class SocketsHandler
{
protected SocketsHandler(SocketsManager sockets)
{
Sockets = sockets;
}
public SocketsManager Sockets {
get; set; }
/// <summary>
/// 连接一个 socket
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public virtual async Task OnConnected(WebSocket socket)
{
await Task.Run(() => {
Sockets.AddSocket(socket); });
}
/// <summary>
/// 断开指定 socket
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public virtual async Task OnDisconnected(WebSocket socket)
{
await Sockets.RemoveSocketAsync(Sockets.GetId(socket));
}
/// <summary>
/// 发送消息给指定 socket
/// </summary>
/// <param name="socket"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessage(WebSocket socket, string message)
{
if (socket.State != WebSocketState.Open) return;
await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)),
WebSocketMessageType.Text, true, CancellationToken.None);
}
/// <summary>
/// 发送消息给指定 id 的 socket
/// </summary>
/// <param name="id"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessage(string id, string message)
{
await SendMessage(Sockets.GetSocketById(id), message);
}
/// <summary>
/// 给所有 sockets 发送消息
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessageToAll(string message)
{
foreach (var connection in Sockets.GetAllConnections()) await SendMessage(connection.Value, message);
}
/// <summary>
/// 接收到消息
/// </summary>
/// <param name="socket"></param>
/// <param name="result"></param>
/// <param name="buffer"></param>
/// <returns></returns>
public abstract Task Receive(WebSocket socket, WebSocketReceiveResult result,
byte[] buffer);
}
}
3、创建 WebSocket
的中间件
// 文件:SocketsManager/SocketsMiddleware.cs
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace WebSockets.Test.SocketsManager
{
public class SocketsMiddleware
{
private readonly RequestDelegate _next;
public SocketsMiddleware(RequestDelegate next, SocketsHandler handler)
{
_next = next;
Handler = handler;
}
private SocketsHandler Handler {
get; }
public async Task InvokeAsync(HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
// 转换当前连接为一个 ws 连接
var socket = await context.WebSockets.AcceptWebSocketAsync();
await Handler.OnConnected(socket);
// 接收消息的 buffer
var buffer = new byte[1024 * 4];
// 判断连接类型,并执行相应操作
while (socket.State == WebSocketState.Open)
{
// 这句执行之后,buffer 就是接收到的消息体,可以根据需要进行转换。
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
switch (result.MessageType)
{
case WebSocketMessageType.Text:
await Handler.Receive(socket, result, buffer);
break;
case WebSocketMessageType.Close:
await Handler.OnDisconnected(socket);
break;
case WebSocketMessageType.Binary:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
else
{
await _next(context);
}
}
}
}
4、创建 WebSocket
管理子类
可以创建多个,用于个性化设置,主要是上面设置了接收的抽象方法,所以必须要重写 Receive
方法。如果不需要的话,其实把基类的抽象去掉,直接在基类中写也可以。
为了展示效果,添加了加入和离开时的消息提示。同时接收到的消息直接转发给所有人。
// 文件: Handlers/WebSocketMessageHandler.cs
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using WebSockets.Test.SocketsManager;
namespace WebSockets.Test.Handlers
{
public class WebSocketMessageHandler : SocketsHandler
{
public WebSocketMessageHandler(SocketsManager.SocketsManager sockets) : base(sockets)
{
}
public override async Task OnConnected(WebSocket socket)
{
await base.OnConnected(socket);
var socketId = Sockets.GetId(socket);
await SendMessageToAll($"{
socketId}已加入");
}
public override async Task OnDisconnected(WebSocket socket)
{
await base.OnDisconnected(socket);
var socketId = Sockets.GetId(socket);
await SendMessageToAll($"{
socketId}离开了");
}
public override async Task Receive(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
{
var socketId = Sockets.GetId(socket);
var message = $"{
socketId} 发送了消息:{
Encoding.UTF8.GetString(buffer, 0, result.Count)}";
await SendMessageToAll(message);
}
}
}
5、创建注入扩展
直接在 Startup.cs
中写也无不可,但这是好习惯,将每个注入内容单独写到文件。
// 文件:Extensions/SocketsExtension.cs
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using WebSockets.Test.SocketsManager;
namespace WebSockets.Test.Extensions
{
public static class SocketsExtension
{
public static IServiceCollection AddWebSocketManager(this IServiceCollection services)
{
services.AddTransient<SocketsManager.SocketsManager>();
var exportedTypes = Assembly.GetEntryAssembly()?.ExportedTypes;
if (exportedTypes == null) return services;
foreach (var type in exportedTypes)
if (type.GetTypeInfo().BaseType == typeof(SocketsHandler))
services.AddSingleton(type);
return services;
}
public static IApplicationBuilder MapSockets(this IApplicationBuilder app, PathString path,
SocketsHandler socket)
{
return app.Map(path, x => x.UseMiddleware<SocketsMiddleware>(socket));
}
}
}
6、配置 Startup.cs
将上面的内容注入到启动项中即可。
在 ConfigureServices
中添加:
services.AddWebSocketManager();
然后在 Configure
中添加:
app.UseWebSockets();
app.MapSockets("/ws", serviceProvider.GetService<WebSocketMessageHandler>());
即可。
如果提示 serviceProvider
找不到,在 Configure
的参数中添加:
IServiceProvider serviceProvider
即可。
完整的内容如下:
public IConfiguration Configuration {
get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddWebSocketManager();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseWebSockets();
// 配置路径
app.MapSockets("/ws", serviceProvider.GetService<WebSocketMessageHandler>());
app.UseStaticFiles();
}
7、测试
以上内容已经完成,现在可以跑起来。为了测试,编写一个最简单的页面。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket web client</title>
</head>
<body>
<h1>WebSocket Web Client</h1>
<br />
<input type="text" placeholder="enter your message" id="message">
<button id="sendBtn">Send</button>
<ul id="messageList"></ul>
<script>
// 根据实际地址和端口进行修改,其他内容无需修改
const uri = "ws://localhost:5000/ws";
socket = new WebSocket(uri);
socket.onopen = function (e) {
console.log("websocket estabished!");
}
socket.onclose = function (e) {
console.log('websocket closed!');
}
socket.onmessage = function (e) {
appendItem(list, e.data);
console.log(e.data);
}
const list = document.getElementById("messageList");
const btn = document.getElementById("sendBtn");
btn.addEventListener("click", function () {
console.log("sending message~~~");
var messgae = document.getElementById("message");
socket.send(message.value)
})
function appendItem(list, message) {
const li = document.createElement("li");
li.appendChild(document.createTextNode(message));
list.appendChild(li);
}
</script>
</body>
</html>
测试效果:
有了上面的示例,一个最简单的聊天室模型已经可以实现了。
.NET Core 学习笔记之 WebSocketsSample
1. 服务端
代码如下:
Program:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace WebSocketsServer
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
Startup:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketsServer
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// configure keep alive interval, receive buffer size
app.UseWebSockets();
app.Map("/samplesockets", app2 =>
{
// middleware to handle websocket request
app2.Use(async (context, next) =>
{
if (context.WebSockets.IsWebSocketRequest)
{
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await SendMessagesAsync(context, webSocket, loggerFactory.CreateLogger("SendMessages"));
}
else
{
await next();
}
});
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Web Sockets sample");
});
}
private async Task SendMessagesAsync(HttpContext context, WebSocket webSocket, ILogger logger)
{
var buffer = new byte[4096];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
if (result.MessageType == WebSocketMessageType.Text)
{
string content = Encoding.UTF8.GetString(buffer, 0, result.Count);
if (content.StartsWith("REQUESTMESSAGES:"))
{
string message = content.Substring("REQUESTMESSAGES:".Length);
for (int i = 0; i < 10; i++)
{
string messageToSend = $"{
message} - {
i}";
if (i == 9)
{
messageToSend += ";EOS"; // send end of sequence to not let the client wait for another message
}
byte[] sendBuffer = Encoding.UTF8.GetBytes(messageToSend);
await webSocket.SendAsync(new ArraySegment<byte>(sendBuffer), WebSocketMessageType.Text, endOfMessage: true, CancellationToken.None);
logger.LogDebug("sent message {0}", messageToSend);
await Task.Delay(1000);
}
}
if (content.Equals("SERVERCLOSE"))
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Bye for now", CancellationToken.None);
logger.LogDebug("client sent close request, socket closing");
return;
}
else if (content.Equals("SERVERABORT"))
{
context.Abort();
}
}
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
}
}
}
}
launchSettings.json
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:58167/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WebSocketsServer": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:58168/"
}
}
}
2. 客户端
Program.cs
代码如下:
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketClient
{
class Program
{
static async Task Main()
{
Console.WriteLine("Client - wait for server");
Console.ReadLine();
await InitiateWebSocketCommunication("ws://localhost:58167/samplesockets");
//"ws://localhost:6295/samplesockets"
//http://localhost:58167/
Console.WriteLine("Program end");
Console.ReadLine();
}
static async Task InitiateWebSocketCommunication(string address)
{
try
{
var webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(address), CancellationToken.None);
await SendAndReceiveAsync(webSocket, "A");
await SendAndReceiveAsync(webSocket, "B");
await webSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("SERVERCLOSE")),
WebSocketMessageType.Text,
endOfMessage: true,
CancellationToken.None);
var buffer = new byte[4096];
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer),
CancellationToken.None);
Console.WriteLine($"received for close: " +
$"{
result.CloseStatus} " +
$"{
result.CloseStatusDescription} " +
$"{
Encoding.UTF8.GetString(buffer, 0, result.Count)}");
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure,
"Bye",
CancellationToken.None);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static async Task SendAndReceiveAsync(WebSocket webSocket, string term)
{
byte[] data = Encoding.UTF8.GetBytes($"REQUESTMESSAGES:{
term}");
var buffer = new byte[4096];
await webSocket.SendAsync(new ArraySegment<byte>(data),
WebSocketMessageType.Text,
endOfMessage: true,
CancellationToken.None);
WebSocketReceiveResult result;
bool sequenceEnd = false;
do
{
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer),
CancellationToken.None);
string dataReceived = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"received {
dataReceived}");
if (dataReceived.Contains("EOS"))
{
sequenceEnd = true;
}
} while (!(result?.CloseStatus.HasValue ?? false) && !sequenceEnd);
}
}
}