C# 允许处理非响应消息的请求-响应逻辑
我有一个套接字应用程序,它允许数千个客户端连接。它将它们存储在C# 允许处理非响应消息的请求-响应逻辑,c#,.net,sockets,logic,communication,C#,.net,Sockets,Logic,Communication,我有一个套接字应用程序,它允许数千个客户端连接。它将它们存储在ConcurrentDictionary中,仅在请求-响应情况下运行: 当我需要数据时,我会找到相关的套接字并发送请求,询问我需要的数据 发送请求后,我接收字节,直到它发送响应。然后我停止接收 像这样: public Task<Message> Request(int clientId, Message message) { Socket client; return Clients.TryGetVal
ConcurrentDictionary
中,仅在请求-响应情况下运行:
- 当我需要数据时,我会找到相关的套接字并发送请求,询问我需要的数据
- 发送请求后,我接收字节,直到它发送响应。然后我停止接收
public Task<Message> Request(int clientId, Message message)
{
Socket client;
return Clients.TryGetValue(clientId, out client)
? RequestInternal(client, message);
: _EmptyTask;
}
public async Task<Message> RequestInternal(Socket client, Message message)
{
await SendAsync(client, message).ConfigureAwait(false);
return await ReceiveOneAsync(client).ConfigureAwait(false);
}
有了它,我可以接收和处理客户机发送的所有信息,但要区分为回复我的请求而发送的消息和发送的消息,因为客户机需要这样做是一个问题
我想我可以在请求中发送一个唯一的标识符字节(对于特定的客户机是唯一的)。然后客户端可以在其响应中将该标识符发送回我,我可以使用它来区分响应
ProcessMessage(Message msg)
{
// msg is a message from msg.Sender.
if (msg.Id == 0)
{
// msg is not a response, do processing.
}
else
{
// msg is a response to the message that's sent with msg.Id.
// Find the request that:
// * ...is made to msg.Sender
// * ...and has the msg.Id as identifier.
// And process the response according to that.
}
}
这意味着我还必须存储请求。
以下是RequestInternal
的假设版本:
编辑:在Stephen Cleary回答后,将Wait
呼叫替换为Wait
s
和请求
类:
private sealed class Request
{
public readonly byte Id;
public readonly Socket Client;
public readonly Message Message;
public readonly TaskCompletionSource<Message> Source;
public Request(Socket client, Message message)
{
Client = client;
Message = message;
Source = new TaskCompletionSource<Message>();
// Obtain a byte unique to that socket...
Id = GetId(client);
}
}
虽然我不知道什么样的收集类型请求
编辑:我使用了一个ConcurrentDictionary
,其中Key
是一个私有结构,具有Int32
(套接字id)和字节
(消息id)字段。它还实现了我几年前写的IEquatable
,它解决了一些常见问题(例如,和)。代码示例都使用Socket
类,但相同的概念适用于所有TCP/IP套接字
关于协议设计和请求/响应匹配,总体方法听起来不错。您需要确保线程安全(例如,请求
可能是一个ConcurrentDictionary
)。另外,您应该Wait
SendAsync
而不是调用Wait
我曾经尝试过但尚未投入生产的另一种方法是基于。您可以为每个客户机创建一个表示“输出”的块,为“输入”创建另一个块。然后,您可以在此基础上分层您的消息框架,并在此基础上分层您的请求/响应匹配,然后将任何剩余的(未经请求的)消息发送到单个共享的缓冲块
因此,您的“最终用户”API最终将如下所示:
// Send a request and asynchronously receive a matching response.
Task<Message> RequestAsync(int clientId, Message message);
// Endpoint for unsolicited messages.
IReceivableSourceBlock<Tuple<int, Message>> UnsolicitedMessages { get; }
//发送请求并异步接收匹配的响应。
任务请求异步(int-clientId,Message);
//未经请求的消息的终结点。
IReceivableSourceBlock未经请求的消息{get;}
然后,您可以将一个操作块
连接到未经请求的消息
,以便在有人进来时执行委托。我几年前写的一篇文章解决了一些常见问题(例如,和)。代码示例都使用Socket
类,但相同的概念适用于所有TCP/IP套接字
关于协议设计和请求/响应匹配,总体方法听起来不错。您需要确保线程安全(例如,请求
可能是一个ConcurrentDictionary
)。另外,您应该Wait
SendAsync
而不是调用Wait
我曾经尝试过但尚未投入生产的另一种方法是基于。您可以为每个客户机创建一个表示“输出”的块,为“输入”创建另一个块。然后,您可以在此基础上分层您的消息框架,并在此基础上分层您的请求/响应匹配,然后将任何剩余的(未经请求的)消息发送到单个共享的缓冲块
因此,您的“最终用户”API最终将如下所示:
// Send a request and asynchronously receive a matching response.
Task<Message> RequestAsync(int clientId, Message message);
// Endpoint for unsolicited messages.
IReceivableSourceBlock<Tuple<int, Message>> UnsolicitedMessages { get; }
//发送请求并异步接收匹配的响应。
任务请求异步(int-clientId,Message);
//未经请求的消息的终结点。
IReceivableSourceBlock未经请求的消息{get;}
然后,您可以将一个ActionBlock
连接到UnsolicitedMessages
,以便在每次有人进来时执行委托
ProcessMessage(Message msg)
{
if (msg.Id == 0)
OnReceived(msg); // To raise an event.
else
{
// Method to find a request using msg.Sender and msg.Id
var request = Requests.Find(msg);
if (request != null)
request.Source.SetResult(msg);
}
}
// Send a request and asynchronously receive a matching response.
Task<Message> RequestAsync(int clientId, Message message);
// Endpoint for unsolicited messages.
IReceivableSourceBlock<Tuple<int, Message>> UnsolicitedMessages { get; }