我有一个websocket应用程序,它是一个OWIN中间件。当请求进入时,会启动websocket处理程序的新实例,然后在这样的循环中等待icoming消息:
var buffer = new byte[1024*64];
Tuple<ArraySegment<byte>, WebSocketMessageType> received;
do
{
received = await _webSocket.ReceiveMessage(buffer, _cancellationToken.Token);
if (received.Item1.Count > 0 && someConditionForCallingDoSomething)
{
await DoSomething(received.Item1);
}
else if(isAnswer)
{
QueueAnswer(received.Item1);
}
} while (received.Item2 != WebSocketMessageType.Close);
当数据可用时,_webSocket.ReceiveMessage
返回的任务将完成。
DoSomething
处理其数据,然后通过websocket连接发送内容。然后它应该通过websocket连接等待消息。处理完此消息后,它应该做一些工作并返回(任务)。
也许这个小图解释了它更容易:
_______________
| DoSomething |
|-------------|
| Work ---------> Send WS message
| |
| ?? |
| |
| More Work <------- Receive WS message
| | |
| V |
| return; |
|_____________|
Do some work with the data
|
V
Send a message
|
V
Wait for an answer
|
V
Process answer
|
V
finish
我试图等待答案:
var answerCancel = new CancellationTokenSource();
answerCancel.CancelAfter(30 * 1000);
var answer = await Task.Run(async () =>
{
string tmpAnswer = null;
while (!_concurrentAnswerDict.TryGetValue(someKey, out tmpAnswer)) {
await Task.Delay(150, answerCancel.Token);
}
return tmpAnswer;
}, answerCancel.Token);
但这似乎会阻止,直到任务被取消。当我调试程序时,我在30秒后看到QueueAnswer
的调用。我想,Task.Run
将在一个新线程中运行该函数,但它似乎没有。从Task.Run
阻塞的角度来看,我觉得它不合理,因为我等待DoSomething
的执行,因此接收新消息也会被阻止。
我的问题是:我如何实现这样的行为?在完成之前,如何使DoSomething
等待另一个websocket消息?
提前感谢您的每一个提示
的Lukas
答案 0 :(得分:2)
首先,我建议使用SignalR,因为他们会为你处理很多这些难题。但如果你想自己做,请继续阅读...
此外,我假设“工作”和“回答”消息可以按任何顺序到达同一个Web套接字,并且您正在使用_concurrentAnswerDict
来协调来自的“传出”问题消息DoSomething
收到“回复”消息。
在这种情况下,您将需要一个独立于DoSomething
的“websocket reader”任务;您无法让读者await
DoSomething
,因为这样会阻止您阅读答案。我认为这是你遇到的主要问题。
这是不 await
任务可以接受的极少数情况之一。假设DoSomething
将捕获自己的异常并处理日志记录等等,那么我们可以将其视为独立的“main”并忽略它返回的任务:
var buffer = new byte[1024*64];
Tuple<ArraySegment<byte>, WebSocketMessageType> received;
do
{
received = await _webSocket.ReceiveMessage(buffer, _cancellationToken.Token);
if (received.Item1.Count > 0 && someConditionForCallingDoSomething)
{
var _ = DoSomething(received.Item1);
}
else if(isAnswer)
{
QueueAnswer(received.Item1);
}
} while (received.Item2 != WebSocketMessageType.Close);
这应该允许QueueAnswer
在DoSomething
尚未完成时运行。
我想,Task.Run将在一个新线程中运行该函数,但它似乎没有。从Task.Run阻塞的角度来看,我认为它无法正常工作,因为我等待DoSomething的执行,因此接收新消息也将被阻止。
Task.Run
在另一个线程中运行。但DoSomething
(异步)等待它完成,并且读取循环(异步)等待DoSomething
完成,然后才能读取下一条消息。
其他说明:
while (!_concurrentAnswerDict.TryGetValue(someKey, out tmpAnswer)) {
await Task.Delay(150, answerCancel.Token);
}
这对我来说似乎很奇怪。我建议使用TaskCompletionSource<Answer>
代替Answer
的密钥字典。然后,QueueAnswer
将调用TaskCompletionSource<Answer>.SetResult
,此代码将等待TaskCompletionSource<Answer>.Task
(如果需要超时,则等待Task.Delay
。)
答案 1 :(得分:0)
您应该使用信号r并在客户端接收响应。 或者最好在客户端发送请求并在客户端接收。
在向服务器发送请求并等待响应时,使用await会阻塞。