如何在不阻塞的情况下建立和验证客户端连接的最佳方法是什么?

3

我有一个TcpListener服务器,可以通过调用server.AcceptTcpClientAsync()与客户端建立连接。不幸的是,会有随机的、不需要关心的流量连接到服务器,因此我添加了一个方法Task<bool> Handshake(TcpClient client)来验证客户端是否应该能够与服务器通信。

获取连接的朴素方式为:

async Task<TcpClient> GetClient(TcpListener server)
{
   while (true)
   {
      var client = await server.AcceptTcpClientAsync();
      if (await Handshake(client))
      {
         return client;
      }
      client.Dispose();
   }
}

不幸的是,握手过程需要一定的时间才能超时等待虚假连接,如果这个时间比下一个连接出现的时间长,那么挂起的连接将会堆积并且无法得到服务。

我知道像Task.WhenAny这样的API可以同时运行多个异步操作,但我似乎无法找到一个适合这种连接过程的模型。

我想要的:

  1. 只有一个成功的连接被期望。
  2. 当一个连接到达时,尝试进行握手。 如果握手成功,则返回并且处理任何其他挂起的连接。如果没有成功,则处理该连接。
  3. 正在进行握手不应阻止循环接受新的连接。
  4. 握手进行中的数量始终可能为0、1或大于1。

有没有好的方法来用代码表达这个想法?


经过这么多次尝试,我相信我的答案现在应该可以正常工作了。 - Code Name Jack
2个回答

1
您可以使用 Channel 实现安全的跨线程通信。
var channel = Channel.CreateBounded<TcpClient>(100);
var writer = channel.Writer;
var reader = channel.Reader;

_ = GetClient(server);

var successClient = await Task.Run(async () =>
{
    await foreach (var client in reader.ReadAllAsync())
    {
        if (await Handshake(client))
        {
            return client;
        }
        client.Dispose();
    }
    return null;
});

//mark the channel that no more data is going to be written to it
writer.TryComplete();

//run through the remaining clients in the channel and dispose of them
await foreach (var client in reader.ReadAllAsync())
{
    client.Dispose();
}

//use successClient here


async Task<TcpClient> GetClient(TcpListener server)
{
    while (true)
    {
        var client = await server.AcceptTcpClientAsync();
        await writer.WriteAsync(client);
    }
}


0

首先,您可以使用 Task.Run 来执行任务,这样您就不必在那里等待。然后,您可以使用 ContinueWith 作为回调来处理成功的握手。接下来,您可以维护一个连接列表以进行处理。 此外,我建议在循环时添加一些延迟。


async Task<TcpClient> GetClient(TcpListener server)
{ 
   List<Task<Client>> _clients=new(); 
   bool handshakeCompleted=false;
   Client workingClient=null;
   while (!handshakeCompleted)
   {
      var client = Task.Run(async ()=> await server.AcceptTcpClientAsync());
      client.ContinueWith(async ()=>
      {
         if (await Handshake(client))
         {
           handshakeCompleted=true;
           workingClient=client;
         }
      });
      _clients.Add(client);
      await Task.Delay(10);
   }
 foreach(var c in _clients.Where(c!=workingClient)) c.Dispose();
 return workingClient;
}

有趣,但这会迅速使 _clients 膨胀,因为有很多操作在等待实际上没有连接的客户端。 - Mason Wheeler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接