在下面的代码中,Connect()
似乎工作正常。但是webSocket.ReceiveAsync()
中的ReceiveMessage()
似乎在服务器没有提供任何内容的时候设置cancelToken.IsCancellationRequested = true
和webSocket.State = Aborted
。与此同时,相同的ReceiveAsync()
调用会引发一个(无用的)OperationCanceledException
,没有内部异常。
我使用了我认为是相同的WebSocket交互而没有这个问题的同一服务器,但使用更简单的异步代码。直到我尝试将它转换成一个可观察到我有这个问题。
它被Rx取消了,还是取消了?我发现在.NET WebSockets中通过未观察到的异常(http://www.salmanq.com/blog/5-things-you-probably-didnt-know-about-net-websockets/2013/04/)引起了这样的事情,但在添加TaskScheduler.UnobservedTaskException += ExceptionLogger
之后我找不到任何证据。
有什么想法吗?
public static IObservable<string> GetWebSocketStream(Uri uri)
{
var buffer = new byte[2048];
return Observable
.Using(cancelToken => Connect(uri, cancelToken),
(webSocket, cancelToken) => Task.FromResult(
Observable.Defer(
() => ReceiveMessage(webSocket, buffer, cancelToken).ToObservable()
).Repeat()
)
).Publish().RefCount();
}
private static async Task<ClientWebSocket> Connect(Uri uri, CancellationToken token)
{
var webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(uri, token).ConfigureAwait(false);
return webSocket;
}
private static async Task<string> ReceiveMessage(ClientWebSocket webSocket, byte[] buffer, CancellationToken cancelToken)
{
var segment = new ArraySegment<byte>(buffer);
var message = new StringBuilder();
WebSocketReceiveResult result;
do
{
result = await webSocket.ReceiveAsync(segment, cancelToken).ConfigureAwait(false);
message.Append(Encoding.UTF8.GetString(segment.Array, 0, result.Count));
} while (!cancelToken.IsCancellationRequested && !result.EndOfMessage);
return message.ToString();
}
编辑:为了澄清,我的问题/问题可能与BOTH WebSockets和Rx有关。以下类似的代码在同一设备上保持打开状态并接收数据:
public static void Test() {
var ts = new CancellationTokenSource();
AsyncTest(new Uri("ws://server/test/"), ts.Token).Wait();
}
public static async Task AsyncTest(Uri uri, CancellationToken token) {
var socket = await GetConnectedWebSocket(uri, token);
var buffer = new byte[4000];
while (!token.IsCancellationRequested)
{
try
{
var msg = await ReceiveMessage(socket, buffer, token);
Debug.WriteLine(msg);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
throw;
}
}
}
答案 0 :(得分:0)
The problem is with cancelToken
passed to ReceiveMessage
function. In Rx implementation before Task
finish (in your case Task.FromResult
) cleanup code is executed and as a port if it cancelToken
is disposed. I had the same problem when I tried to use such code:
return Observable.Using(cancelToken => Connect(uri, authToken, cancelToken), (webSocket, token) =>
Task.FromResult(Observable.Create<string>((observer) => WebSocketReceiver.WebSocketReceiverSubscribe(observer, webSocket, cancelToken))))
.Publish().RefCount();
but this code works:
return Observable.Using(cancelToken => Connect(uri, authToken, cancelToken), (webSocket, token) =>
Task.FromResult(Observable.Create<string>((observer, ct) => WebSocketReceiver.WebSocketReceiverSubscribe(observer, webSocket, ct))))
.Publish().RefCount();
As I can see Observable.Defer
doesn't pass CancellationToken
object so you will have to create external one and pass it to ReceiveMessage
. Alternatively you can play with Observable.Create
as in my example.