Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 允许处理非响应消息的请求-响应逻辑_C#_.net_Sockets_Logic_Communication - Fatal编程技术网

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; }