Какой идиоматический способ программирования асинхронных сокетов в Delphi?

StackOverflow https://stackoverflow.com/questions/37185

Вопрос

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

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

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

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

Это было полезно?

Решение

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

http://www.overbyte.be/frame_index. HTML? redirTo = / продукты / ics.html

Другие советы

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

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

  

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

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

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

  

@Mike: " ... необходимость уничтожения сокетов для освобождения потоков ... ".

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

  

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

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

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

Проблема C10K

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

@Chris Miller. То, что вы указали в своем ответе, фактически неточно.

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

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

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

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

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

@Roddy - я уже прочитал ссылки, на которые вы указываете, на них обе ссылаются из презентации Пола Тима "Тысячи потоков и блокировка ввода-вывода. Старый способ написания 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 является механическим), поэтому не требуется никаких затрат на ясность кода.

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

  

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

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

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

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

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

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

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

Удачи!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top