服务端使用Cowboy.WebSockets监听并处理连接,前端使用浏览器内置的WebSocket。
客户端连接服务端:成功。
客户端向服务端发送消息:服务端可正常接收。
服务端向客户端发送消息:第1次,客户端正常接收;第2次发送时报错。
错误发生于服务端:
Exception Type: System.IO.IOException
Message: 无法从传输连接中读取数据: 你的主机中的软件中止了一个已建立的连接。。
TargetSite: System.IAsyncResult BeginRead(Byte[], Int32, Int32, System.AsyncCallback, System.Object)
HelpLink: NULL
Source: System
Exception StackTrace:
--------------------------------------
在 System.Net.Sockets.NetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
在 System.IO.Stream.<>c.<BeginEndReadAsync>b__43_0(Stream stream, ReadWriteParameters args, AsyncCallback callback, Object state)
在 System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance,TArgs](TInstance thisRef, TArgs args, Func`5 beginMethod, Func`3 endMethod)
在 System.IO.Stream.BeginEndReadAsync(Byte[] buffer, Int32 offset, Int32 count)
在 System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
在 System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count)
在 Cowboy.WebSockets.AsyncWebSocketSession.<Process>d__65.MoveNext() 位置 Cowboy.WebSockets\Cowboy.WebSockets\Server\AsyncWebSocketSession.cs:行号 398
服务端监听代码:(完全照抄Cowboy的官方示例代码)
var catalog = new AsyncWebSocketServerModuleCatalog();
catalog.RegisterModule(new TestWebSocketModule());
var config = new AsyncWebSocketServerConfiguration();
var _server = new AsyncWebSocketServer(22222, catalog, config);
_server.Listen();
服务端发送代码:
_server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text));
或
session.SendTextAsync(text); //session为用于测试的活动连接
前端代码:
var ws = new WebSocket('ws://127.0.0.1:22222');
console.log('ws连接状态:' + ws.readyState);
ws.onopen = function () {
console.log('ws连接状态:' + ws.readyState);
}
ws.onmessage = function (data) {
console.log('ws接收消息:', data);
}
ws.onclose = function () {
console.log(`ws连接状态:${ws.readyState}`);
}
ws.onerror = function (err) {
console.log('ws发生异常:', err);
}
function onsend() {
var box = document.getElementById('msg');
ws.send(box.value);
box.value = '';
}
在两次发送过程中,输出了如下日志:
第1次发送
WebSocket server [0.0.0.0:22222] broadcasts binary -> [第1次发送].
第2次发送
WebSocket server [0.0.0.0:22222] broadcasts binary -> [第1次发送].
2022-05-31 17:01:04.7188|DEBUG|Cowboy.WebSockets.AsyncWebSocketSession|Session [SessionKey[c5c9cd05-b53b-4bcb-9a27-30ac0e5e2477], RemoteEndPoint[127.0.0.1:30999], LocalEndPoint[127.0.0.1:22222]] received client side close frame but no status code.
2022-05-31 17:01:04.7228|DEBUG|Cowboy.WebSockets.AsyncWebSocketSession|Session [SessionKey[c5c9cd05-b53b-4bcb-9a27-30ac0e5e2477], RemoteEndPoint[127.0.0.1:30999], LocalEndPoint[127.0.0.1:22222]] sends server side close frame [InvalidPayloadData] [].
能看到第1次发送时,不仅结果是成功的,日志也很干净。而第2次发送,就多了两条日志,似乎是收到了客户端发过来的“请求关闭”指令,我认为这很奇怪,猜测可能是协议不兼容导致,浏览器端大概是不会发送这种指令的,因为经测试使用其他库(如Fleck)作为服务端时不会发生类似异常。
值得注意的是,用Cowboy提供的ClientTest作为客户端测试时,并不会出现以上问题,两端可进行反复通信均正常。
问题猜测1
难道Cowboy只能对具有特定协议的客户端搭建WebSocket通道,对浏览器无效吗?问题猜测2
是不是Cowboy本身的异步代码导致了读取和写入的冲突呢?但是看这个项目已经很多年了,据说在处理高并发连接方面是最成熟的。
特来求解,请不吝赐教!
附Cowboy代码仓库连接(含示例):
https://github.com/gaochundong/Cowboy.WebSockets