Обращение к задающим вопросы по сокетам.
От: Maxim S. Shatskih Россия  
Дата: 12.06.04 17:16
Оценка: 271 (28) +3 -2
#Имя: FAQ.sockets.answers
Изучать сокеты имело бы смысл в такой последовательности.

Сначала 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. Именно по этим причинам.
Занимайтесь LoveCraftом, а не WarCraftом!
Re: В FAQ срочно!
От: adontz Грузия http://adontz.wordpress.com/
Дата: 13.06.04 00:26
Оценка: +2
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: Обращение к задающим вопросы по сокетам.
От: SM Россия  
Дата: 13.06.04 03:58
Оценка: 9 (1)
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Лучшие ИМХО документы по Berkeley sockets — MSDN Library и маны от FreeBSD.


Еще можно почитать здесь (на русском )

bye.
... << RSDN@Home 1.1.3 stable >>
Re[2]: Обращение к задающим вопросы по сокетам.
От: Maxim S. Shatskih Россия  
Дата: 13.06.04 08:26
Оценка:
SM>Еще можно почитать здесь (на
>русском )

Можно.

Вроде в тексте ляпов нет, но много BSDизмов (из числа socket options), которые не будут работать в Windows, и не факт, что будут работать в Linux.
Занимайтесь LoveCraftом, а не WarCraftом!
Re: Обращение к задающим вопросы по сокетам.
От: zelyony  
Дата: 13.06.04 08:43
Оценка: 31 (3)
согласен, что в большинстве случаев хватает варианта "клиент — поток" (для UDP один поток может обрабатывать все запросы). и проблем нет, и легко для отладки, и понятный код

но бывают варианты, когдла создавать тысячи потоков невозможно по некоторым причинам поэтому придумали (на наши головы) асинхронный ввод\вывод

есть много вариантов: select, WSAAsyncSelect, WSAEventSelect, модель c OVERLAPPED, completion routinеs, сompletion ports.

просто добавлю:

AcceptEx — хорош когда вам помимо коннекшена необходимо получить и "запрос" (несколько байт, в которых клиент "говорит" что ему надо). в функции можно указать буфер. часто клиент обращается к серверу с запросом (тот же HTTP), а функция позволяет отложить создание потоков(ниток) (если они вообще будут создаваться) до получения запроса

тот же CompletionPort для того и создавался, чтобы реальную работу выполняли несколько потоков (которые обычно уже созданы, а создание тож требует времени)

итак, о проблемах и граблях и кривых руках, кривых дровах сетевых карточек, которые вам придётся решать с completion port:
AcceptEx — работает (а что, может не работать? читай дальше) если передавать буфера только для пары адресов (размер в буфере (буфер один и для адресов и для данных) должен быть как минимум на 16 байт больше чем максимальный адрес в данном протоколе (например для TCP — sizeof(sockaddr_in)+16)
WSASend — работает нормально (не забудьте передать правильные указатели всем параметрам — только один (мы же работаем с completion ports) может быть NULL — указатель на completion routine)
WSARecv (возможно сюда же относится AcceptEx c передачей буфера для данных) — хмм... ну что можно сказать? скажу с чем столкнулся:
— в-нулевых, не забудьте, что все эти 3 функции могут закончится синхронно — ну это легко и понятно (прям как с синхронными сокетами);
— во-первых, клиент передавал данные из буфера размером 64Кб (с накоплением, т.е. через Н-ое время, скажем, раз в секунду), на сервере из сокета принимал порциями по 8Кб (напоминаю, через порты завершения) (по наличию данных). в самом начале, если в сокете есть данные, то мы их берём синхронно, потом данные заканчиваются и чтение выполняется асинхронно. данные снова приходят, и что получается? асинхронное чтение завершается, в нашем буфере 8Кб — мы их обрабатываем. но на самом деле в буфере сокета есть ещё данные и система (или дрова) решают нам рассказать об этом и порт завершения запускает второй поток, и передаёт ему следующие 8Кб в ТОТ же самый буфер, и теперь у нас два потока обрабатывают "уникальные" данные в ОДНОМ буфере (мы то вызывали только один раз WSARecv и передали (в данном случае) 1 WSABUF), ВАУ! но в сокете же есть ещё данные, запускается третий поток, которому передаются слдедующие 8Кб в ТОТ же буфер. мало того что они работают с ОДНИМ буфером, дык, мы ещё не можем (система сама выбирает очередность выполнения потоков) регулировать что кому и когда пришло. намёк поняли? хорошо. как разрешить кривые дрова или этот кошмар? пока до меня дошло, что происходит (думал: один асинхронный вызов — один колбэк в порте завершения... наивный... полдня на смарку), в общем установил буфер в сокете SO_RCVBUF) такого же размера, который передаю на чтение (8Кб)
— во-вторых, что-то ещё хреновое есть: приходящие от клиента данные пишу в файл, файл получается бОльшего размера (просто сказка какая-то!). хорошо работает только если размеры буферов клиента и сервера совпадают (что не всегда удовлетворимо, клиента пишу не я и не я определяю какого размера буфер ему передавать в send)

PS ну накопаю ещё сказок — рассажу
Posted via RSDN NNTP Server 1.9 alpha
Re[2]: Обращение к задающим вопросы по сокетам.
От: Maxim S. Shatskih Россия  
Дата: 13.06.04 09:38
Оценка:
>нашем буфере 8Кб — мы их обрабатываем. но на самом деле в буфере сокета есть ещё данные
>и система (или дрова) решают нам рассказать об этом и порт завершения запускает второй
>поток,

А каким это образом IOCP стал уметь рожать нити? Или это делает твой модуль по работе с IOCP?

>и передаёт ему следующие 8Кб в ТОТ же самый буфер


А каким это образом ядро может использовать ТОТ же буфер под IO, что и в только что завершенной операции? Ни одна компонента кернела такого бреда по собственной инициативе не сделает.

Зато это может быть твой код, который второй раз позвал WSARecv с тем же буфером по какой-то причине.

>(система сама выбирает очередность выполнения потоков)


Да, а еще система сама выбирает очередность завершения IRPов, и нет никакой гарантии, что они не реордерятся внутри IOCP. Есть только гарантия, что на пути вниз — от WSARecv до заполнения данными — они не реордерятся.

Вывод простой — крайне аккуратно надо слать 2 pending WSARecv в один сокет. Иначе потом поток из кусочков не соберешь.

>или этот кошмар? пока до меня дошло, что происходит (думал: один асинхронный вызов —

>один колбэк в порте завершения... наивный... полдня на смарку),

Ну да. Так и должно быть, если, конечно, WSARecv синхронно не завершился.

Z>- во-вторых, что-то ещё хреновое есть: приходящие от клиента данные пишу в файл, файл

>получается бОльшего размера (просто сказка какая-то!).

Ну баги это попробуй ради прикола убрать IOCP и перейти на Berkeley — что получится?
Занимайтесь LoveCraftом, а не WarCraftом!
Re[2]: Обращение к задающим вопросы по сокетам.
От: zelyony  
Дата: 14.06.04 09:22
Оценка:
> WSARecv (возможно сюда же относится AcceptEx c передачей буфера для данных) — хмм... ну что можно сказать? скажу с чем столкнулся:
> — в-нулевых, не забудьте, что все эти 3 функции могут закончится синхронно — ну это легко и понятно (прям как с синхронными сокетами);
> — во-первых, бла-бла-бла...
> — во-вторых, что-то ещё хреновое есть...
>

итак, даже если вызов WSARecv говорит, что он выполнился синхронно, порт завершения вызывает и асинхронные потоки (смотрел в оба): вот такой код, для синхронности и асинхронности:
void RecordSession::recvNext() {
    _over.Internal = _over.InternalHigh = 0;
    _event.reset();
    dword dw = 0, f = 0;
    while (true) {
        int res = WSARecv( _sock.handle(), &_wbuf, 1, &dw, &f, &_over, 0);
        ODS( "WSA_RECV " << ((res) ? "ASYNC" : "SYNC") << " \ttid=" << GetCurrentThreadId());
        return; // неважно какой результат - выходим и всё
        if (res) break;
        if (dw) {
            ODS( "WRITE1 at " << _timestamp << " \t" << dw << " bytes");
            if (_file->write( _timestamp, _wbuf.buf, dw)) _timestamp += dw;
            else { fire( StateError); return; }
        } else { fire( StateRemoteFinished); return; }
        dw = f = 0;
    }
//    if ((!WSAGetOverlappedResult( _sock.handle(), &_over, &dw, os_false, &f))
//        && (WSAGetLastError() != WSA_IO_INCOMPLETE)) { fire( StateRemoteFinished); return; }
}

void RecordSession::asyncIO( HANDLE h, dword bytes, OVERLAPPED* pO, int error) {
    if (error) {
        ODS( "ERROR = " << bytes << " \terror = " << error);
        fire( StateRemoteFinished); // StateError
    }
    else if (bytes != -1) { // когда сам отменяю запросы - посылаю -1
        if (h == (HANDLE)_sock.handle()) {
            if (!bytes) fire( StateRemoteFinished);
            else {
                ODS( "WRITE2 at pos=" << _timestamp << " \tbytes=" << bytes << " \ttid=" << GetCurrentThreadId());
                if (!_file->write( _timestamp, _wbuf.buf, bytes)) {
                    fire( StateError);
                    return;
                }
                _timestamp += bytes;
                recvNext();
            }
        } else { // accept
            fire( StateRecording);
            _file = new WavFileWrite( _fileName);
            if (!_file->isOpen()) fire( StateError);
            else recvNext();
        }
    }
}


лог такой (часть):
00000039 12:59:03.151 WSA_RECV SYNC tid=3860
00000040 12:59:03.151 WRITE2 at pos=0 bytes=16000 tid=3860
00000041 12:59:03.151 WSA_RECV SYNC tid=3860
00000042 12:59:03.151 WRITE2 at pos=16000 bytes=16000 tid=3860
00000043 12:59:03.151 WSA_RECV SYNC tid=3860
00000044 12:59:03.151 WRITE2 at pos=32000 bytes=16000 tid=3860
00000045 12:59:03.151 WSA_RECV SYNC tid=3860
00000046 12:59:03.151 WRITE2 at pos=48000 bytes=16000 tid=3860
00000047 12:59:03.183 WSA_RECV SYNC tid=3860
00000048 12:59:03.183 WRITE2 at pos=64000 bytes=1536 tid=3860
00000049 12:59:03.183 WSA_RECV ASYNC tid=3860
00000050 12:59:03.198 WRITE2 at pos=65536 bytes=16000 tid=3860
00000051 12:59:03.198 WSA_RECV SYNC tid=3860
00000052 12:59:03.198 WRITE2 at pos=81536 bytes=16000 tid=348
00000053 12:59:03.198 WSA_RECV SYNC tid=348
00000054 12:59:03.198 WRITE2 at pos=97536 bytes=16000 tid=348
00000055 12:59:03.198 WSA_RECV SYNC tid=348
00000056 12:59:03.198 WRITE2 at pos=113536 bytes=16000 tid=348
00000057 12:59:03.198 WSA_RECV SYNC tid=348
00000058 12:59:03.198 WRITE2 at pos=129536 bytes=1536 tid=348
00000059 12:59:03.198 WSA_RECV ASYNC tid=348
00000060 12:59:03.261 WRITE2 at pos=131072 bytes=16000 tid=348
00000061 12:59:03.261 WSA_RECV SYNC tid=348
00000062 12:59:03.261 NEW COMPLETION PORT THREAD = 3816
00000063 12:59:03.261 WRITE2 at pos=147072 bytes=16000 tid=3860
00000064 12:59:03.261 WSA_RECV SYNC tid=3860
00000065 12:59:03.261 WRITE2 at pos=163072 bytes=16000 tid=348
00000066 12:59:03.261 WSA_RECV SYNC tid=348
00000067 12:59:03.261 WRITE2 at pos=179072 bytes=16000 tid=3860
00000068 12:59:03.261 WSA_RECV SYNC tid=3860
00000069 12:59:03.261 WRITE2 at pos=195072 bytes=1536 tid=348
00000070 12:59:03.261 WSA_RECV ASYNC tid=348
00000071 12:59:03.323 WRITE2 at pos=196608 bytes=16000 tid=348
00000072 12:59:03.323 WSA_RECV SYNC tid=348
00000073 12:59:03.323 WRITE2 at pos=212608 bytes=16000 tid=3816
00000074 12:59:03.323 WSA_RECV SYNC tid=3816

вот так (игнор результата WSARecv) записалось правильно (случайно, наверное, один TCP-шный поток данных (не нитка) обрабатывался четырьмя вычислительными потоками (нитками) — порядок записи мог измениться и доступ к _timestamp не синхронизирован)

и вообще, при возврате из WSARecv SOCKET_ERROR, WSAGetLastError возвращает 0, поэтому поставил ещё WSAGetOverlappedResult (после неё ошибка нормальная)

могу добавить что стоит виндоуз 2003, сетевуха "3Com EtherLink XL 10/100 PCI TX NIC (3C905B-TX)" дрова версии 4.25.0.0 от Microsoft

мдя... может всё таки у меня бред?
Posted via RSDN NNTP Server 1.9 alpha
Re: Вот ещё статья...
От: Alexander Shargin Россия RSDN.ru
Дата: 14.06.04 11:56
Оценка: +1
как раз для начинающих написана.
http://www.rsdn.ru/article/unix/sockets.xml
Автор(ы): Александр Шаргин
Дата: 16.05.2001
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re: недопонимаю :-(
От: Serjio Россия  
Дата: 22.07.04 05:43
Оценка:
>Еще момент. Zero-copy send в Windows. Ставится SO_SNDBUF в нуль. После чего все send() и прочие WSASend() и WriteFile() гонят DMA сетевой карты прямо по этому буферу, ничего не копируя. Недостаток — операция завершится, только когда придут все ACKи на эти данные. Потому в этом случае лучше не слать помалу, а слать эдак по 64 кило, и слать сразу кучей overlapped sends.

то есть открываем сокет hSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP); либо hSocket := socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
(указывая тем самым использовать TCP/UDP протокол)
После чего, полученному сокету, ставим SO_SNDBUF в нуль.

И "... все send() и прочие WSASend() и WriteFile() гонят DMA сетевой карты прямо по этому буферу, ничего не копируя" ?

С заголовками ?
И "Да" и "Нет" приводят к противоречию.
видимо, что-то не сходится в исходном утвердении.
Только на РСДН помимо ответа на вопрос, можно получить еще список орфографических ошибок и узнать что-то новое из грамматики английского языка (c) http://www.rsdn.ru/forum/cpp/4720035.1.aspx
Автор: ZOI4
Дата: 28.04.12
Re: Обращение к задающим вопросы по сокетам.
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 27.12.04 03:10
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Berkeley вполне себе многонитевы, и, если несколько нитей к одному сокету на лазают — то вполне себе решение.


так "вполне себе" или все-таки нужна синхронизация? что-то я не въехал
Re[2]: Обращение к задающим вопросы по сокетам.
От: butcher Россия http://bu7cher.blogspot.com
Дата: 27.12.04 07:08
Оценка:
Здравствуйте, Odi$$ey, Вы писали:

OE>так "вполне себе" или все-таки нужна синхронизация? что-то я не въехал

Я думаю, можно провести аналогию с дескриптором файла. Т.е. синхронизация всётаки нужна..

Нет ничего невозможного..
Re[2]: Обращение к задающим вопросы по сокетам.
От: IGor_79 Украина  
Дата: 27.12.04 07:14
Оценка: 18 (1)
Здравствуйте, Odi$$ey, Вы писали:

OE>Здравствуйте, Maxim S. Shatskih, Вы писали:


MSS>>Berkeley вполне себе многонитевы, и, если несколько нитей к одному сокету на лазают — то вполне себе решение.


OE>так "вполне себе" или все-таки нужна синхронизация? что-то я не въехал


Сами сокеты уже синхронизированы (унутри реализации), поэтому к одному сокету можно спокойно
обращаться из разных потоков без опасений. Но как правило синхронизация все же нужна на уровне
протокола, то есть бессмысленно вызывать recv для получения ответа на запрос раньше, чем вы
послали сам запрос.
Re: Обращение к задающим вопросы по сокетам.
От: nii_im_b Мухосранск  
Дата: 28.12.04 09:42
Оценка:
Господа!

может встрял не в ту ветку , тогда уберите, но...

здесь можно почиать про layered service provider (LSP). правда на английском. если кто-то обладает инфой на русском, поделитесь плз.
вопрос ведь достаточно интересный и довольно часто задаваемый
Re[2]: Обращение к задающим вопросы по сокетам.
От: IGor_79 Украина  
Дата: 29.12.04 07:19
Оценка:
Здравствуйте, nii_im_b, Вы писали:

__>Господа!


__>может встрял не в ту ветку , тогда уберите, но...


__>здесь можно почиать про layered service provider (LSP). правда на английском. если кто-то обладает инфой на русском, поделитесь плз.

__>вопрос ведь достаточно интересный и довольно часто задаваемый

Есть переведенная на русский язык книга — "Программирование в сетях Microsoft Windows", Э.Джонса и Д.Оланда.
Там целая глава посвящена Winsock2 SPI. Кстати, Оланд является и соавтором статьи приведенной вами.

ЗЫ
Русского электронного перевода у меня нет, но книгу свободно можно купить, или может в инете где-то есть...
Re: Обращение к задающим вопросы по сокетам.
От: AlexDav Россия  
Дата: 08.11.05 11:48
Оценка: :)
Я начинающий и ,как вы думаете, я понял хоть что-нибудь?!
Re[2]: Обращение к задающим вопросы по сокетам.
От: Аноним  
Дата: 08.11.05 12:53
Оценка:
Z>WSARecv (возможно сюда же относится AcceptEx c передачей буфера для данных) — хмм... ну что можно сказать? скажу с чем столкнулся:
Z>- в-нулевых, не забудьте, что все эти 3 функции могут закончится синхронно — ну это легко и понятно (прям как с синхронными сокетами);

ни разу не смог добиться синхронного завершения, даже если данные уже пришли, отрабатывал IO_PENDING и пакет в порт

Z>- во-первых, клиент передавал данные из буфера размером 64Кб (с накоплением, т.е. через Н-ое время, скажем, раз в секунду), на сервере из сокета принимал порциями по 8Кб (напоминаю, через порты завершения) (по наличию данных). в самом начале, если в сокете есть данные, то мы их берём синхронно, потом данные заканчиваются и чтение выполняется асинхронно. данные снова приходят, и что получается? асинхронное чтение завершается, в нашем буфере 8Кб — мы их обрабатываем. но на самом деле в буфере сокета есть ещё данные и система (или дрова) решают нам рассказать об этом и порт завершения запускает второй поток, и передаёт ему следующие 8Кб в ТОТ же самый буфер, и теперь у нас два потока обрабатывают "уникальные" данные в ОДНОМ буфере (мы то вызывали только один раз WSARecv и передали (в данном случае) 1 WSABUF), ВАУ!


Вау! один WSARecv успел дважды завершиться, синхронно и асинхронно? а код ошибки — сумма?

Z>но в сокете же есть ещё данные, запускается третий поток, которому передаются слдедующие 8Кб в ТОТ же буфер. мало того что они работают с ОДНИМ буфером, дык, мы ещё не можем (система сама выбирает очередность выполнения потоков) регулировать что кому и когда пришло. намёк поняли?


намек поняли, пихаешь все время в WSARecv один и тот же буфер, не поработав с данными, ай-яй-яй

Z>хорошо. как разрешить кривые дрова или этот кошмар?


кривые руки?

Z>- во-вторых, что-то ещё хреновое есть: приходящие от клиента данные пишу в файл, файл получается бОльшего размера (просто сказка какая-то!). хорошо работает только если размеры буферов клиента и сервера совпадают (что не всегда удовлетворимо, клиента пишу не я и не я определяю какого размера буфер ему передавать в send)


это не неудовлетворительно, это ваще кол с минусом


Z>PS ну накопаю ещё сказок — рассажу


сказочник
Re[3]: Обращение к задающим вопросы по сокетам.
От: zelyony  
Дата: 08.11.05 14:41
Оценка:
Здравствуйте, Аноним, Вы писали:

во-первых, пост давнишний (ничего, обновили)

Z>>WSARecv (возможно сюда же относится AcceptEx c передачей буфера для данных) — хмм... ну что можно сказать? скажу с чем столкнулся:

Z>>- в-нулевых, не забудьте, что все эти 3 функции могут закончится синхронно — ну это легко и понятно (прям как с синхронными сокетами);

А>ни разу не смог добиться синхронного завершения, даже если данные уже пришли, отрабатывал IO_PENDING и пакет в порт

да просто я лучше добивался


Z>>- во-первых, клиент передавал данные из буфера размером 64Кб (с накоплением, т.е. через Н-ое время, скажем, раз в секунду), на сервере из сокета принимал порциями по 8Кб (напоминаю, через порты завершения) (по наличию данных). в самом начале, если в сокете есть данные, то мы их берём синхронно, потом данные заканчиваются и чтение выполняется асинхронно. данные снова приходят, и что получается? асинхронное чтение завершается, в нашем буфере 8Кб — мы их обрабатываем. но на самом деле в буфере сокета есть ещё данные и система (или дрова) решают нам рассказать об этом и порт завершения запускает второй поток, и передаёт ему следующие 8Кб в ТОТ же самый буфер, и теперь у нас два потока обрабатывают "уникальные" данные в ОДНОМ буфере (мы то вызывали только один раз WSARecv и передали (в данном случае) 1 WSABUF), ВАУ!


А>Вау! один WSARecv успел дважды завершиться, синхронно и асинхронно? а код ошибки — сумма?

про то, что именно ОДИН WSARecv завершился синхронно и ассинхронно — не сказано
а судя по тому, что код ошибки был 0 — то было произведением.. или беззнаковым целочисленным делением мЕньшего на бОльшее, но не на 0.. или производной от (WSA)GetLastError()..)

Z>>но в сокете же есть ещё данные, запускается третий поток, которому передаются слдедующие 8Кб в ТОТ же буфер. мало того что они работают с ОДНИМ буфером, дык, мы ещё не можем (система сама выбирает очередность выполнения потоков) регулировать что кому и когда пришло. намёк поняли?


А>намек поняли, пихаешь все время в WSARecv один и тот же буфер, не поработав с данными, ай-яй-яй

ситуация здесь описывается с приемом данных, на один запрос чтения порт выдавал несколько откликов
(может с разными размерами данных? но эти грабли я должен был пройти уже несколько лет назад (1 — в моем понимании не несколько))

Z>>хорошо. как разрешить кривые дрова или этот кошмар?


А>кривые руки?

ну на прямую из геометрии мои руки точно не похожи

Z>>- во-вторых, что-то ещё хреновое есть: приходящие от клиента данные пишу в файл, файл получается бОльшего размера (просто сказка какая-то!). хорошо работает только если размеры буферов клиента и сервера совпадают (что не всегда удовлетворимо, клиента пишу не я и не я определяю какого размера буфер ему передавать в send)


А>это не неудовлетворительно, это ваще кол с минусом

да уж, подробностей не помню, но вполне возможно, что то был стрим небинарный и увеличивал размер данных, вставляя вместо '\r' "\r\n"


Z>>PS ну накопаю ещё сказок — рассажу


А>сказочник

МТС: ты — лучше!
БиЛайн: спасибо! я знаю!
(PS пост о баяне = баян)

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

PS тяжелый случай, когда пациент считает себя психиатром
PPS PS вслух никому
Re[4]: Обращение к задающим вопросы по сокетам.
От: Аноним  
Дата: 09.11.05 08:42
Оценка:
Здравствуйте, zelyony, Вы писали:
...

ну без кода эти выяснялки ни к чему не приведут, а кода этого наверно уж и в живых-то нет...
так что — проехали,
но вот как мне на IOCP получить синхронный вызов WSARecv() ? Научите....
Re: Обращение к задающим вопросы по сокетам.
От: Аноним  
Дата: 17.09.07 13:24
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Еще момент. Производительность TCP на паттерне "короткий вопрос — короткий ответ" очень часто зависит от алгоритма Nagle. Причем тормоза в ответах. Немножко упрощаю, но примерно так — ответ на первый вопрос пройдет на ура, а ответ на второй затормозится до прихода ACK во встречном потоке — реально это произойдет только в третьем вопросе, или же после задержки в полсекунды. В итоге, если вопрошающая сторона наглухо стоит, ожидая ответа — третьего вопроса не будет, и мы получаем по транзакции в полсекунды, независимо от скорости сети


Не совсем понял этот пример. Почему может затормозиться ответ на второй запрос, ведь во втором запросе придет ACK на первый ответ и окно отправки на сервере очистится? Тогда второй ответ будет отправлен немедленно, как и первый и все остальные.
Поясните, пожалуйста.
Re[2]: Обращение к задающим вопросы по сокетам.
От: Michael Chelnokov Украина  
Дата: 17.09.07 13:48
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Не совсем понял этот пример. Почему может затормозиться ответ на второй запрос, ведь во втором запросе придет ACK на первый ответ и окно отправки на сервере очистится? Тогда второй ответ будет отправлен немедленно, как и первый и все остальные.


Не факт что немедленно. Алгоритм Нагла (Нейгла? Нэйгла?) старается отправить как можно больше данных в одном сегменте. Это хорошо помогает когда передается именно поток данных, а не (небольшие по обьему) запрос/ответ/запрос/ответ/... От размера неподтвержденных данных, конечно, тоже зависит, но не только от него. Как указал Максим, TCP на отправляющей стороне может притормозить отправку в надежде что поступят еще данные и их удастся отправить в том же сегменте.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.