Как распространять входящие соединения tcplistener по потокам в .NET?
При использовании Net.Sockets.TcpListener, что является лучшим способом обработки входящих соединений (.AcceptSocket) в отдельных потоках?
Идея состоит в том, чтобы начать новый поток, когда новое входящее соединение будет принято, тогда как tcplistener останется доступным для дальнейших входящих подключений (и для каждого нового входящего соединения создается новый поток). Вся связь и завершение с клиентом, инициировавшим соединение, будут обрабатываться в потоке.
Оценивается пример С# кода VB.NET.
Ответы
Ответ 1
Код, который я использовал, выглядит следующим образом:
class Server
{
private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false);
public void Start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 5555);
listener.Start();
while(true)
{
IAsyncResult result = listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
connectionWaitHandle.WaitOne(); // Wait until a client has begun handling an event
connectionWaitHandle.Reset(); // Reset wait handle or the loop goes as fast as it can (after first request)
}
}
private void HandleAsyncConnection(IAsyncResult result)
{
TcpListener listener = (TcpListener)result.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(result);
connectionWaitHandle.Set(); //Inform the main thread this connection is now handled
//... Use your TcpClient here
client.Close();
}
}
Ответ 2
Я считаю, что вы делаете это так же, как и любая другая асинхронная операция в .NET: вы вызываете версию метода BeginXxx, в данном случае BeginAcceptSocket. Ваш обратный вызов будет выполняться в пуле потоков.
Объединяемые потоки обычно масштабируются намного лучше, чем потоки за соединение: после того, как вы преодолеете несколько десятков подключений, система работает намного сложнее при переключении между потоками, чем при выполнении фактической работы. Кроме того, каждый поток имеет свой собственный стек, размер которого обычно составляет 1 МБ (хотя он зависит от флагов ссылки), который должен быть найден в виртуальном адресном пространстве 2 ГБ (в 32-битных системах); на практике это ограничивает вас менее чем 1000 потоками.
Я не уверен, что в настоящее время .NET threadpool использует его, но Windows имеет объект ядра, называемый портом завершения ввода-вывода, который помогает в масштабируемом вводе-выводе. Вы можете связать потоки с этим объектом, и с ним могут быть связаны запросы ввода-вывода (включая прием входящих соединений). Когда завершается ввод-вывод (например, соединение), Windows выдает ожидающий поток, но только если число текущих запущенных потоков (не заблокированных по какой-либо другой причине) меньше установленного предела масштабируемости для порта завершения. Как правило, вы устанавливаете это на небольшое число из числа ядер.
Ответ 3
Я хотел бы предложить отличный подход:
Мое предложение использует только два потока. * один поток проверяет наличие подключений. * Когда новое соединение открыло эту информацию, она записывается в общую структуру данных, которая содержит все текущие открытые соединения. * Второй поток перечисляет, что структура данных и для каждого открытого соединения получают данные, отправленные и отправляющие ответы.
Это решение более масштабируемо по потоку и, если оно реализовано, должно иметь лучшую производительность, а затем открывать новый поток за каждое открытое соединение.
Ответ 4
Там отличный пример в Cookbook O'Reilly С# 3.0. Вы можете загрузить сопроводительный источник из http://examples.oreilly.com/9780596516109/CSharp3_0CookbookCodeRTM.zip
Ответ 5
Я бы использовал threadpool, так что вам не придется запускать новый поток каждый раз (так как это довольно дорого). Я также не стал бы бесконечно ждать дополнительных соединений, так как клиенты не могут закрыть свои соединения. Как вы планируете каждый раз маршрутизировать клиента в тот же поток?
Извините, у вас нет образца.