Какой идиоматический способ сделать асинхронное программирование сокетов в Delphi? - PullRequest
10 голосов
/ 01 сентября 2008

Как обычно люди, пишущие сетевой код в Delphi, используют асинхронный ввод-вывод сокетов в стиле Windows?

Вот мое предыдущее исследование этого вопроса:

Компоненты Indy кажутся полностью синхронными. С другой стороны, хотя модуль ScktComp действительно использует WSAAsyncSelect, он в основном только асинхронизирует приложение мультиплексированных сокетов в стиле BSD. Вы получаете дамп в обратном вызове одного события, как если бы вы только что вернулись из select () в цикле, и вам нужно выполнить всю навигацию конечного автомата самостоятельно.

Ситуация .NET значительно лучше, с Socket.BeginRead / Socket.EndRead, где продолжение передается непосредственно в Socket.BeginRead, и именно здесь вы берете обратно. Продолжение, закодированное как замыкание, очевидно, имеет весь контекст, который вам нужен, и даже больше.

Ответы [ 10 ]

2 голосов
/ 01 сентября 2008

@ Родди - Синхронные сокеты - это не то, что мне нужно. Запись целого потока ради возможно долгоживущего соединения означает, что вы ограничиваете количество одновременных соединений числом потоков, которое может содержать ваш процесс. Поскольку потоки используют много ресурсов - зарезервированное адресное пространство стека, выделенную память стека и переходы ядра для переключения контекста - они не масштабируются, когда вам нужно поддерживать сотни соединений, а то и меньше, а то и больше.

2 голосов
/ 01 сентября 2008

Я обнаружил, что Indy, с самого начала более простой концепцией, неудобно управлять из-за необходимости уничтожать сокеты для освобождения потоков при завершении работы приложения. Кроме того, у меня перестала работать библиотека Indy после обновления патча ОС. ScktComp хорошо работает для моего приложения.

1 голос
/ 13 января 2009

Есть бесплатные компоненты сокета IOCP (порты завершения): http://www.torry.net/authorsmore.php?id=7131 (с исходным кодом)

"Набережных Сергей Николаевич. Высокий сервер сокетов производительности на основе Порт завершения Windows и с использованием Расширения Windows Socket. IPv6 поддерживается. «

Я нашел это, когда искал более качественные компоненты / библиотеку, чтобы реструктурировать мой маленький сервер обмена мгновенными сообщениями. Я еще не пробовал, но это выглядит хорошо закодировано как первое впечатление.

1 голос
/ 04 сентября 2008

@ Родди - я уже прочитал ссылки, на которые вы указываете, на них обе ссылаются из презентации Пола Тима «Тысячи потоков и блокировка ввода-вывода - старый способ написания Java-серверов снова новый».

Однако некоторые вещи, которые не обязательно выпрыгивают из презентации Пола, заключаются в том, что он указал -Xss: 48k для JVM при запуске и что он предполагает, что реализация NIO JVM эффективна для того, чтобы он быть действительным сравнением.

Indy не определяет аналогично сжатый и строго ограниченный размер стека. Нет вызовов BeginThread (подпрограммы создания потока Delphi RTL, которую вы должны использовать в таких ситуациях) или CreateThread (необработанный вызов WinAPI) в кодовой базе Indy.

Размер стека по умолчанию хранится в PE, и для компилятора Delphi он по умолчанию равен 1 МБ зарезервированного адресного пространства (пространство за страницей фиксируется ОС в 4K-блоках; фактически, компилятору необходимо сгенерировать код для прикасайтесь к страницам, если в функции> 4K локальных элементов, потому что расширение контролируется ошибками страниц, но только для самой нижней (сторожевой) страницы в стеке). Это означает, что вы исчерпаете адресное пространство после максимально 2000 одновременных потоков, обрабатывающих соединения.

Теперь вы можете изменить размер стека по умолчанию в PE с помощью директивы {$ M minStackSize [, maxStackSize]}, но это повлияет на все потоки , включая основной поток. Я надеюсь, что вы не делаете много рекурсии, потому что 48K или (аналогично) не много места.

Теперь, прав ли Пол в отношении неэффективности асинхронного ввода-вывода для Windows, в частности, я не уверен на 100% - я должен был бы измерить это, чтобы быть уверенным. Однако я знаю, что аргументы о том, что многопоточное программирование проще, чем асинхронное программирование на основе событий, представляют ложную дихотомию .

Асинхронному коду не нужно , чтобы быть основанным на событиях; оно может быть основано на продолжении, как в .NET, и если вы укажете закрытие в качестве продолжения, вы получите состояние, сохраняемое для вас бесплатно. Более того, преобразование из кода в линейном стиле потока в асинхронный код в стиле продолжения может быть выполнено механически с помощью компилятора (преобразование CPS является механическим), поэтому не требуется никаких затрат для ясности кода.

1 голос
/ 04 сентября 2008

@ Крис Миллер - То, что вы указали в своем ответе, на самом деле неточно.

Асинхронность в стиле сообщений Windows, доступная через WSAAsyncSelect, действительно в значительной степени является обходным решением из-за отсутствия надлежащей модели потоков в Win 3.x дней.

.NET Begin / End, однако, не с использованием дополнительных потоков. Вместо этого он использует перекрывающийся ввод-вывод, используя дополнительный аргумент в WSASend / WSARecv, в частности перекрывающуюся процедуру завершения, чтобы указать продолжение.

Это означает, что стиль .NET использует поддержку асинхронного ввода-вывода ОС Windows для предотвращения прожига потока путем блокировки на сокете.

Поскольку потоки, как правило, дороги (если вы не указываете очень маленький размер стека для CreateThread), блокировка потоков на сокетах не позволит вам масштабировать до 10 000 одновременных соединений.

Вот почему важно использовать асинхронный ввод-вывод, если вы хотите масштабировать, а также почему .NET не , повторяю, не , просто "используя потоки, [...] просто управляемые платформой ".

1 голос
/ 02 сентября 2008

«Синхронные сокеты - это не то, что мне нужно».

Понятно - но я думаю, что в этом случае ответ на ваш первоначальный вопрос заключается в том, что просто отсутствует Delphi идиома для асинхронного ввода-вывода сокета, потому что это на самом деле очень узкое и необычное требование.

В качестве дополнительной проблемы вы можете найти эти ссылки интересными. Они оба немного староваты и более * nxy, чем Windows. Второй подразумевает, что в правильной среде потоки могут быть не такими плохими, как вы думаете.

Проблема C10K

Почему события - плохая идея (для серверов с высоким уровнем параллелизма)

1 голос
/ 01 сентября 2008

Как обычно пишут люди сетевой код в Delphi использовать Windows-стиль перекрывается асинхронным гнездо ввода / вывода?

Что ж, Indy уже давно является «стандартной» библиотекой для сокетов ввода-вывода - и она основана на блокировании сокетов. Это означает, что если вы хотите асинхронного поведения, вы используете дополнительные потоки для соединения / чтения / записи данных. На мой взгляд, это на самом деле главное преимущество, поскольку нет необходимости управлять каким-либо видом навигации по конечным автоматам или беспокоиться о процедурах обратного вызова или подобных вещах. Я считаю, что логика моей «читающей» ветки менее загромождена и гораздо более переносима, чем позволяют неблокирующие сокеты.

Indy 9 был в основном взрывобезопасен, быстр и надежен для нас. Однако переход к Indy 10 для Tiburon вызывает у меня небольшое беспокойство.

@ Майк: «... нужно убивать сокеты, чтобы освободить темы ...».

Это заставило пойти "а?" пока я не вспомнил, что наша библиотека потоков использует технику, основанную на исключениях, для безопасного уничтожения «ожидающих» потоков. Мы вызываем QueueUserAPC , чтобы поставить в очередь функцию, которая вызывает исключение C ++ (НЕ производное от класса Exception), которое должно быть перехвачено только нашей процедурой обертки потока. Все деструкторы вызываются так, что все потоки завершаются чисто и приводятся в порядок на выходе.

0 голосов
/ 12 сентября 2008

С классами ScktComp вам необходимо использовать сервер ThreadBlocking, а не тип сервера NonBlocking. Используйте событие OnGetThread, чтобы передать параметр ClientSocket новому потоку вашего устройства. После создания экземпляра унаследованного экземпляра TServerClientThread вы создадите экземпляр TWinSocketStream (внутри потока), который вы можете использовать для чтения и записи в сокет. Этот метод избавляет вас от попыток обработки данных в обработчике событий. Эти потоки могут существовать только в течение короткого периода времени, необходимого для чтения или записи, или зависать на время для повторного использования.

Тема написания сокет-сервера довольно обширна. Есть много методов и приемов, которые вы можете выбрать для реализации. Метод чтения и записи в один и тот же сокет в TServerClientThread прост и подходит для простых приложений. Если вам нужна модель для обеспечения высокой доступности и параллелизма, вам необходимо изучить шаблоны, подобные шаблону Proactor.

Удачи!

0 голосов
/ 10 сентября 2008

Для асинхронного соединения попробуйте ICS

http://www.overbyte.be/frame_index.html?redirTo=/products/ics.html

0 голосов
/ 03 сентября 2008

Indy использует синхронные сокеты, потому что это более простой способ программирования. Асинхронная блокировка сокетов была добавлена ​​в стек winsock еще в дни Windows 3.x. Windows 3.x не поддерживает потоки, и там вы не можете сделать сокет ввода-вывода без потоков. Дополнительную информацию о том, почему Indy использует модель блокировки, см. В этой статье .

.

В вызовах .NET Socket.BeginRead / EndRead используются потоки, они управляются только платформой, а не вами.

@ Родди, Indy 10 входит в состав Delphi начиная с Delphi 2006. Я обнаружил, что переход с Indy 9 на Indy 10 является простой задачей.

...