Изучать сокеты имело бы смысл в такой последовательности. Сначала Berkeley, то есть вызовы, написанные сплошь маленькими буквами, и без использования неблокирующего IO. Это неважно, что вы любите Си++ и врапперы — напишите врапперы вокруг Berkeley. Berkeley вполне себе многонитевы, и, если несколько нитей к одному сокету на лазают — то вполне себе решение. Колоссальное количество задач решается на них. Без использования FIONBIO. Без использования микрософтных наворотов типа WSARecv или AcceptEx. Лучшие ИМХО документы по Berkeley sockets — MSDN Library и маны от FreeBSD. Потом можно изучать nonblocking. Кстати, FIONREAD — он же СиШарпный socket.Available — нужен практически только для nonblocking моды. Для изучения микрософтных наворотов нужно в первую очередь понимать, как вообще работает async IO в Win32. Преимуществ async IO по сути немного, учитывая, что ОС многонитевая, и можно просто еще одну нитку создать. а) можно использовать IOCP, что хорошо для некоторых сценариев. б) меньше раз зовется memcpy() внутри AFD и TCP стека. Если на сокете постоянно висит overlapped read — то AFD копирует данные сразу из сетевых пакетов в user buffer, связанный с этим readом. Если же не висит — то AFD приходится использовать свой внутренний буфер, и получается на одно memcpy() больше. Теперь сравним select() и сценарий с кучей overlapped IO на куче сокетов, завязанных на один IOCP. Во время останова в select() на сокетах не висят операции чтения, потому приходится использовать лишнюю буферизацию. IO completion port — хорошая штука, но реально бессмысленна для сценариев, где на нем будет ждать 1 нитка. Например, если по нитке на сокет. Тогда уж лучше использовать APC, а в нитке вместо GetQueuedCompletionStatus написать SleepEx(0, TRUE). Еще есть сообщения Windows, мне они не нравятся, но тоже механизм для этого случая, особенно, если та же нитка и UI крутит, и с сокетом работает. Еще момент. Zero-copy send в Windows. Ставится SO_SNDBUF в нуль. После чего все send() и прочие WSASend() и WriteFile() гонят DMA сетевой карты прямо по этому буферу, ничего не копируя. Недостаток — операция завершится, только когда придут все ACKи на эти данные. Потому в этом случае лучше не слать помалу, а слать эдак по 64 кило, и слать сразу кучей overlapped sends. У меня был код, который легко насыщал 100мегабитную сеть. Протокол, смахивающий на HTTP. Для нормальной работы пришлось а) ставить SO_SNDBUF в большое число б) слать короткий заголовок в) ставить SO_SNDBUF в нуль г) слать длинное тело. Такой, и только такой паттерн мог практически насытить и сеть, и дисковый IO для чтения тела из файла. Еще момент. Производительность TCP на паттерне "короткий вопрос — короткий ответ" очень часто зависит от алгоритма Nagle. Причем тормоза в ответах. Немножко упрощаю, но примерно так — ответ на первый вопрос пройдет на ура, а ответ на второй затормозится до прихода ACK во встречном потоке — реально это произойдет только в третьем вопросе, или же после задержки в полсекунды. В итоге, если вопрошающая сторона наглухо стоит, ожидая ответа — третьего вопроса не будет, и мы получаем по транзакции в полсекунды, независимо от скорости сети Примерно то же самое возникает, если ответ состоит из нескольких мелких sendов — например, если делать ответ HTTP, печатая каждую строку функцией fprintf прямо в сокет. Первая строка прошла, вторая ждет ACKа, а ACK придет в данному случае только как delayed, потому как встречного потока нет — вторая сторона крутится в цикле приема заголовка ответа. В линуксе была опция TCP_CORK специально для такого паттерна, во FreeBSD то же самое достигалось, но чуть иначе, в Windows вроде как вообще нет. Короче, реально написать код, у которого лимит скорости будет упираться в полсекунды на delayed ACK. Далеко не всегда это хорошо. Именно потому X11 — у которого такой паттерн — выключает TCP_NODELAY. Недостаток — сильная фрагментация данных, по сети едут совсем мелкие пакеты с большими заголовками. Еще можно переделать протокол так, чтобы ответ шел не на каждый вопрос, а на пачку. Примерно так сделано в RDP. К чему это все написано. Полно начинающих, которые задают вопросы по коду, в котором используется лишний для задачи функционал. Например, не к месту асинхронный сокет. Не к месту nonblocking IO. Эта задача решалась бы тривиально на самом тупом Berkeley API. Крайне не рекомендую для начинающих MFCшный класс CAsyncSocket. Именно по этим причинам. |