Ваш сервер должен постоянно прослушивать соединения от клиентов. Каждый раз, когда он получает соединение, вы получаете от ОС новый TcpClient
для обработки этого соединения. TcpClient
, который вы получаете от ОС, иногда называется Child TcpClient , потому что он создается портом прослушивания. Но он функционально такой же, как и любой другой сокет.
Этого очень легко достичь с помощью шаблона асинхронного / ожидающего выполнения.
Сначала вам нужен слушатель, который ожидает соединения, каждый раз, когда он получает соединение это передает это другой задаче, которая обрабатывает дочерний TcpClient. Например,
static async Task StartTcpServerAsync()
{
var tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 9999));
tcpListener.Start();
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
// Fire and forget the Child connection
_ = StartChildTcpClientAsync(tcpClient);
}
}
В приведенном выше коде котельной пластины мы полностью забываем о задаче ChildTcpClient. Вы можете или не хотите этого. Если вы делаете задачу полностью самодостаточной (как эта демонстрация), тогда главной задаче, возможно, никогда не потребуется знать, завершена она или нет. Но если вам необходимо обеспечить обратную связь между Задачей ChildTcpClient и основной задачей, вам потребуется предоставить дополнительный код для управления этим, и вы начнете с хранения Задачи, возвращенной из StartChildTcpClientAsync(tcpClient);
, где вы сможете наблюдать Это. Вы можете использовать Task.Run()
здесь, но это не обязательно в консольном приложении.
Теперь, когда у вас есть задача слушателя, вам нужна задача ChildTcpClient, например:
static async Task StartChildTcpClientAsync(TcpClient tcpClient)
{
Console.WriteLine($"Connection received from: {tcpClient.Client.RemoteEndPoint.ToString()}");
using var stream = tcpClient.GetStream();
var buffer = new byte[1024];
while (true)
{
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
await stream.WriteAsync(buffer, 0, bytesRead);
}
}
Это очень простой клиент эхо Tcp. У него нет управления потоком данных и нет обработки ошибок, задача будет просто сидеть и ждать, пока клиент отправит некоторые данные, даже после того, как клиент отключился. Эта задача просто «ждет» бесконечности (или пока не произойдет исключение), поэтому вам нужно добавить сюда некоторый код для управления клиентом, проверить, что клиент все еще подключен, et c et c
Для того, чтобы собрать все это вместе, вам просто понадобится такой основной код:
static async Task Main(string[] args)
{
await StartTcpServerAsync();
// Will never exit unless tcpListener.AcceptTcpClientAsync();
// throws an exception
}
И все, у вас есть консольное приложение, которое ожидает подключения бесконечного числа клиентов, и каждый раз, когда это происходит, это своя задача по работе с IO.