Здравствуйте, Pepel, Вы писали:
P>Спасибо !
Спасибо здесь сверху справа
P>где ее вызывать повтороно ? если accept() блокирует и ее можно успешно в цикде повесить, то как быть с AcceptEx()?
Клиент подключился — создал новый сокет и вызвал AcceptEx.
P>если относительно большой объем данных клиент шлет, даже пару десятков Kb уже наверняка одним вызовом WSARecv не обойтись, или же я не догоняю и уведомление о завершении вычитки станет в IOCP толко когда все данные из сокета будут получены (положим что передаваемый нами в WSARecv буфер приемки достаточно велик) ?
WSARecv — GetQueuedCompletionStatus — WSARecv — GetQueuedCompletionStatus ...
И нету никаких "всех данных". Для системы есть только поток. Только ты определяешь что такое данные, посредством протокола.
Здравствуйте, Pepel, Вы писали:
P>по ходу интересно стало — поскольку accept() блокирует поток, то как народ этот поток завершает корректно ?
поделюсь опытом.
1. первый AcceptEx тоже ЖЕЛАТЕЛЬНО вызывать в рабочих потоках IOCP, потому что это красиво и гламурно.
зачем?
ну во первых если поток который вызвал первый AcceptEx завершится, то все его AcceptEx тут-же отменятся.
поэтому надо точно знать что поток который вызывает первый AcceptEx не завершится раньше времени.
это конечно может быть main thread, но можно просто сделать PostQueuedCompletionStatus со своим событием,
которое быстренько отработает в рабочих потоках, выполнит все что нужно включая первый AcceptEx.
остается только подождать завершения собственного события, или не ждать если вызов из незавершающегося потока.
2. в IOCP использовать только IOCP функции, AcceptEx, ConnectEx, WSARecv, WSASend, TransmitPackets, и.тд, никаких connect, accept
это священное правило IOCP
3. как закрыть сокет? смотри пункт 2!!! ТО ЕСТЬ — DisconnectEx, никаких closesocket на открытых сокетах, closesocket только после отработки
DisconnectEx, т.е только для того чтобы избавиться от хендла.
DisconnectEx это просто супер функция, тот кто ее написал просто красафчег, фукнция просто офигительно красиво завершает соединение по всем
правилам, и после того как соедиенние корректно закрыто, в IOCP получаем уведомление
а ещё хендл можно потом опять заюзать если указать TF_REUSE_SOCKET, ваще прелесть.
Здравствуйте, Pepel, Вы писали:
P>те, кто кидает какашки в Окна очевидно про winsock & iocp не слышали
Да и вообще, не от большого ума.
P>стартую WSARecv — на IOCP ловлю количество принятых, я могу столкнуться с ситуацией "буфер мал" и если да, то где — на IOCP или сразу на вызове WSARecv ?
Для ТСР нет.
P>по поводу WSASend — если я передал туда буфер с 500 байтами к примеру, а на IOCP ловлю запись, что передано всего 300 — такое может быть ?
Может. Писать надо с учетом этого. На практике можешь никогда не столкнуться.
P>я так понимаю в любом случае буфер-передатчик после старта WSASend надо держать и не трогать до тех пор, пока на IOCP не словлю завершение отправки всех его данных — это так ?
Так. Буфер-приемник тоже
Насколько я понял, у вас получается, есть несколько клиентов (на разных сокетах ясное дело), нужно принять он них данные, и потом отправить их серверу (в один сокет). Т.к. потоки байтов нескольких клиентов сливаются в один поток для отправки на сервер, нужно их правильно отправлять серверу, чтобы он на своем удаленном конце их там различил.
С асинхронностью тут противоречий нет. Просто вам нужно реализовать механизм, когда данные от клиентов будут уходить на сервер в правильной последовательности. Делайте асинхронный send, по завершении операции, если не все отправлено — снова асинхронный send, и так до тех пор пока нужный кусок данных от этого клиента не отправится серверу. Данные от остальных клиентов пока ждут своей отправки. Их можно принимать из сети и накапливать в ассоциированном с клиентом буфере. И так последовательно отправлять от каждого клиента. В результате прием не зависит от отравки.
Синхронный send вам тут кажется проще, но ведь он портит ваш асинхронный движок.
Вы учтите, что прием из сети может происходить быстрее чем отправка в сеть, либо ваша прикладная логика может генерить данные быстрее чем они уходят в сеть и тд. Поэтому нужно быть готовым что send не отправит все за один вызов, плюс еще и синхронно. Нужно быть готовым перестать принимать из сети от клиентов (в результате сработает tcp flow control), если они не будут успевать отправляться на сервер. Т.е. они могут к вам от клиентов прибывать быстрее, чем вы их будете успевать отправлять на сервер из-за разной скорости отправки по сети.
Здравствуйте, Pepel, Вы писали: P>запись в очередь порта происходит после завершения асинхронной операции ввода/вывода, т.е применительно к winsock речь идет исключительно о записи/чтении данных сокета и лишь о паре функций WSASend/WSARecv — посредством этих функций мы начинаем асинхронную запись / чтение и узнаем о завершении операции посредством IOCP. Это так ?
Да.
P>GetQueuedCompletionStatus () вернет управление только по факту завершения операций записи/чтения, начатых функциями WSASend/WSARecv на сокете привязанном к порту ?
Да.
P>Но если так, то получается, что параллелизм начинается с момента получения данных от клиента — мой пул потоков может лишь реагировать на факт завершения вычитки из сокета данных, отправленных мне клиентом, но как распаралелить саму вычитку при помощи IOCP ?
Не понял, что значит "вычистка" и зачем тебе ее "параллелить".
Здравствуйте, Pepel, Вы писали:
P>первый вызов AcceptEx() должен происходить не в обработчике IOCP, а все последующие — именно в потоке обработчика IOCP
Да. Причем первый раз можно (нужно) вызвать некоторое количество раз, чтобы подготовить достаточное количество сокетов (если планируется большая нагрузка).
P>WSAAsyncSelect (FD_READ) при таком подходе не нужен, требуется лишь просто вызвать WSAAsyncSelect() для декларации перехода в асинхронный режим работы, а дальше WSARecv() гонять
Забудь про WSAAsyncSelect. Создавай сокет так: WSASocket( PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED )
P>тогда вопрос, а если я стартану WSARecv() вхолостую, т.е. на сокете в котором нет данных для вычитки, запись в очередь IOCP станет когда данные появятся , или же WSARecv() вернет ошибку ?
Ну а как ты думаешь, стоило бы огород городить если б ошибку возвращало?
Здравствуйте, Pepel, Вы писали:
P>по ходу интересно стало — поскольку accept() блокирует поток, то как народ этот поток завершает корректно ?
Закрывает серверный сокет.
Здравствуйте, Pepel, Вы писали:
P>по ходу интересно стало — поскольку accept() блокирует поток, то как народ этот поток завершает корректно ?
Можно использовать неблокирующий сокет для accept(), и/или select() на пару сокетов (серверный+мониторный, на мониторный посылаются данные чтобы прервать ожидание) либо с таймаутом. Это все безотносительно темы треда.
Здравствуйте, Pepel, Вы писали:
>> С IOCP используй AcceptEx. P>а в чем ее фишка, если не сложно в двух словах, и чем хуже вариант WSAAsyncSelect (FD_ACCEPT) + accept ?
В том что по завершению AcceptEx приходит уведомление в порт, что полностью совместимо с остальной логикой работы программы, а не окошечное сообщение в какой-то непонятно зачем сделанный отдельный поток. Почему вы постоянно пытаетесь скрестить ужа и ежа?
Здравствуйте, Pepel, Вы писали:
P>но представим, что клиент на данном сокете закрыл соединение (пусть даже не отправляя мне ничего вообще), — в таком случае в очередь завершения IOCP станет запись с кодом ошибки на сокете , так ?
Здравствуйте, Pepel, Вы писали:
P>есть IOCP и есть привязанный к нему сокет MYSOCK, на котором последовательно стартовал серию асинхронных вызовов : ConnectEx, WSARecv, WSASend, дальше на обработчике IOCP ловлю завершение стартованных мной операций,
ConnectEx — это ты асинхронного клиента пишешь что-ли? В любом случае, шли запросы последовательно, после завершения предыдущего. Когда разберешься — будешь слать как захочешь
P>вопрос в механизме получения мной информации о том, какой из вызовов завершился, понял, что наиболее подходящий вариант да и похоже единственный (!) — параметр CompletionKey функции CreateIoCompletionPort() его тип ULONG_PTR что позволяет передавать даже адрес объекта, но вот народ что именно туда лепит ? какие идентификаторы ?
Я в CompletionKey задаю для сокетов 2 параметра: COMPKEY__CONNECT для серверного и COMPKEY__IO для клиентского. Всю остальную информацию в структуре, производной от OVERLAPPED для каждой операции.
Здравствуйте, Pepel, Вы писали:
P>производная структура — лепим сами структуру некую
P>struct MY_OVERLAPPED {
P>поля базовой структуры OVERLAPPED ;
P>мои добавочные поля ;
P>}
P>а дальше явное приведение типа используем там, где требуется , так ?
В целом да, только удобней так:
typedef struct _PER_IO_DATA : public OVERLAPPED{
...
Здравствуйте, Pepel, Вы писали:
P>я правильно понимаю — нужно обязательно плодить дубликат дискриптора сокета, для того чтобы разруливать 1.и 2. ? т.е. в асинхронных вызовах использовать именно копию дискриптора сокета , так ?
Нет, не надо.
Здравствуйте, Gomes, Вы писали:
P>>я так понимаю в любом случае буфер-передатчик после старта WSASend надо держать и не трогать до тех пор, пока на IOCP не словлю завершение отправки всех его данных — это так ? G>Так. Буфер-приемник тоже
Здравствуйте, Pepel, Вы писали:
P>стартую WSARecv — на IOCP ловлю количество принятых, я могу столкнуться с ситуацией "буфер мал" и если да, то где — на IOCP или сразу на вызове WSARecv ?
если скажешь ему прочитать 0, то буфер точно будет мал
постараюсь донести смысл IOCP, если знать что такое IOCP на пальцах, легче с ним работать
IOCP это механизм синхронизации потоков по работе а асинхронным IO. но можно и использовать на одно-процессорной машине, просто потому что удобно
главное — порт завершения должен быть один на все (сокеты, файлы, трубы и.тд все что асинхронное). видел код где их создавали несколько и причем только для сокетов больше просто смысла нету, только гемор разводить.
проще говоря IOCP это чувак который делает то что ты ему скажешь, ты ему сказал прочитать или записать столько то байт, и дал ему буфер под это дело, пока он тебе буфер обратно не вернет — буфер его, и трогать нельзя.
да, число рабочих потоков должно быть столько сколько процессоров, или в два раза больше чем процессоров, все зависит от реализации программы.
просто если поток может долго ждать критическую секцию например, то потоков лучше побольше чем число процессоров.
на практике самая эффективная модель это — пул буферов.
т.е, тебе надо определить максимальное кол-во байт которое ты принимаешь за запрос, например 8 кило.
создаешь класс-пул который при старте выделяет н-ть буферов (сколько буферов тебе надо для работы зависит от нагрузки)
лучше объясню кодом
class IOBuffer : public OVERLAPPED // OVERLAPPED с буффером в связке
{
PBYTE pData;
public:
IOBuffer(void)
{
pData = new BYTE[8192]
}
// и.тд
}
class IOBufferPool : public ну тупо vector
{
public:
IOBufferPool(int nMaximumBuffers = 10000)
{
for(int i = 0; i < nMaximumBuffers; ++i)
push_back(new IOBuffer);
}
// и.тд
}
void OnDataReceived(IOBuffer* pBuffer, int howMany)
{
DoSomething(pBuffer->getData());
pIOPool->push_back(pBuffer);
}
void OnDataSent(IOBuffer* pBuffer, int howMany)
{
pIOPool->push_back(pBuffer);
}
ну конечно это примитивно описал, но вроде идея понятна.
плюс этого метода в очень высокой производительности, буферов валом и не надо делать new и delete которые любят кушать проц
минусы — фиксированный размер буфера, но это не страшно, 10 гигов можно по 8 килобайт потихоньку принять
и то что количество буферов надо выделять много, чтобы не кончились. т.е надо рассчитать сколько надо программе при полной загрузке.
выделять буферы дополнительно, когда кончаются — плохой тон, если буферы начнут где-то утекать (не возвращаться в пул) то прога упадет только тогда когда вся память уйдет лучше пусть упадет тогда когда они кончатся, будешь знать что мало или где-то утекают.
но пул-буферов это в основном серверная модель, но IOCP и применим в основном в серверах
P>по поводу WSASend — если я передал туда буфер с 500 байтами к примеру, а на IOCP ловлю запись, что передано всего 300 — такое может быть ? я так понимаю в любом случае буфер-передатчик после старта WSASend надо держать и не трогать до тех пор, пока на IOCP не словлю завершение отправки всех его данных — это так ?
вообще вроде бы теоретически IOCP может вернуть что отправил всего 300, но по моему это просто невероятно, так что лучше не напрягаться, и знать что если сказал отправить 500 то отправится 500. если IOCP вернет что отправил 300, то 100% с ошибкой типа порт закрылся или вообще интерфейс пропал
да, пока IOCP не вернет тебе буфер — буфер его, и OVERLAPPED структура тоже.
Здравствуйте, Pepel, Вы писали:
P>Салют друзья-коллеги, тут походу вопросов у меня насобиралось :
P>1. Дескриптор сокета — я так понимаю безотносительно к конструкциям на которые он дает привязку это обычное число unsigned long и, получив его при создании сокета (WSASocket()), могу в рамках процесса смело гонять из функции в функцию до тех пор пока closesocket() на дескрипторе не отработаю, а ф-ция DuplicateHandle() — для межпроцессовой передачи доступа к объектам и нафиг в рамках одного процесса не нужна.
ы, ну само собой, а что кто-то DuplicateHandle в одном процессе юзает?
P>2. Вызов listen(..) — второй параметр задает размер очереди для клиентов на ожидание подключения, смотрел пару примеров народ туда 1 лепит, а почему не SOMAXCONN константу ? — ведь в этом случае согласно хэлпу размер данной очереди будет == вменяемому максимуму. Я так понимаю стартовал я AcceptEx() — словил на IOCP его завершение и тут ж надо как можно быстрее опять стартовать AcceptEx() — а все клиенты которые в этот промежуток будут цепляться станут в очередь — если размерность очереди это позволит, т.е. в listen над погуще лепить второй параметр, никак не 1.
я леплю напрямую 200, где-то вычитал что в серверной винде начиная с w2k это потолок.
The standard method of determining the maximum number of backlogged connections is to use the
SOMAXCONN constant, which is supposed to represent the maximum number that an OS will support
(for example, 5 on Windows 2000 Pro, and 200 on Windows 2000 server)
P>3. Выяснение удачи-неудачи завершения асинхронной операции — я так понял анализа возврата GetQueuedCompletionStatus() предостаточно, вызов WSAGetOverlappedResult() продублирует данную информацию, ничего нового не даст и по сути не нужен.
WSAGetOverlappedResult просто для Overlapped сокетов, а не для IOCP. у IOCP — GetQueuedCompletionStatus.
P>4. Поведение в случае сбоя :
P>сбойнул AcceptEx() — не важно где просек — сразу на старте или на завершении в IOCP обработчике — я так понимаю сервис надо стопорить по любому, потому как дальше бодаться уже дорого и успех не гарантирован ;
в сервере думаю да, бодаться не стоит, просто записать в лог причину.
P>сбойнули WSASend()/WSARecv() — не важно где просек — сразу на старте или на завершении в IOCP обработчике — можно грохнуть клиентский сокет и забыть про него — сервис пусть продолжает работать ;
ну в IOCP сбоит редко что, а все ошибки обычно получаешь уже по GetQueuedCompletionStatus. ну и ес-но легче просто закрыть сокет на любом неудачном результате, а что с ним ещё делать?
P>или все ж аналитика сбоя какая-никакая нужна ?
аналитика нужна для логов, так что всегда нужна.
Здравствуйте, Pepel, Вы писали:
P>2. Вызов listen(..) — второй параметр задает размер очереди для клиентов на ожидание подключения, смотрел пару примеров народ туда 1 лепит, а почему не SOMAXCONN константу ?
Лепи SOMAXCONN, listen сам разберется.
P>Я так понимаю стартовал я AcceptEx() — словил на IOCP его завершение и тут ж надо как можно быстрее опять стартовать AcceptEx()
Так. Только предварительно лучше стартовать несколько AcceptEx. Сколько — зависит от предполагаемой нагрузки.
Здравствуйте, maxlosyam, Вы писали:
M>А точнее лучше столько AcceptEx сколько рабочих потоков, т.е по AcceptEx'у на поток больше смысла нету, быстрее не будет.
Таки лучше поболе, если требуется Само подключение устанавливается в ядре, и от наших потоков не зависит.
Вот что сами Microsoft-ы пишут:
A responsive server must always have enough AcceptEx calls outstanding so that any client connection can be immediately handled. The number of posted AcceptEx operations will depend on the type of traffic your server expects. A high incoming connection rate (because of short-lived connections or spurts in traffic) requires more outstanding AcceptEx calls than an application where the clients connect infrequently. It may be wise to let the number of posted AcceptEx operations vary between application-specific low and high watermarks, and avoid deciding on one fixed number as the magic figure.
http://msdn.microsoft.com/en-us/magazine/cc302334.aspx
M>ну а рабочих потоков лучше столько сколько процессоров (ядер), имхо так оптимально быстро получается.
Общая рекомендация — в 2 раза больше чем ядер. А так зависит от.
Здравствуйте, Pepel, Вы писали:
P>Спасибо всем, вот на практике уперся в то, что поскольку возможна ситуация, когда WSASend() может отправить только часть данных, то мало толку выходит от его вызова в асинхронном режиме — все равно вызывай его пока все не затолкаешь — а если между такими асинхронными заталкиваниями в очередь IOCP станет запись от других обработчиков на передачу данных в этот же сокет. Я понимаю , что ситуация частичной отправки редкая, но по хорошему ее следует учитывать и выстраивать конструктив обработки с учетом множественного вызова WSASend для передачи одного буфера. Тогда не проще ли синхронно в цикле толкать этим же WSASend пока все данные буфера не залетят в сокет ? Тоесть читаем асинхронно с ожиданием завершения на IOCP , а писать все таки в сокет синхронно. Поправьте если что-то не догоняю.
Оппаньки. Только недавно писал, что постиг всю красоту, и тут такое
Откуда у тебя "множественные вызовы WSASend"? Один вызов — одно завершение. Не надо лишнего придумывать.
Здравствуйте, Pepel, Вы писали:
P>да, вообщем, все по делу, действительно в моем случае все равно по любому требуется реализовать "один вызов WSASend() — одно завершение" и помогает удержать эту 'линейку' промежуточная очередь-контейнер данных для отправки в сокет
рекомендую использовать TransmitPackets, он пошустрее
ну а вообще пакеты в твоем случае надо фреймовать, т.е удаленном серверу передаешь не просто пакеты друг за другом, а каждый пакет вкладываешь во фрейм с инфой о том чей это пакет и какой он длинны, и на удаленном уже пакеты из фремов извлекаешь как хочешь.
но если удаленный сервер изменить нельзя, то фигово
Здравствуйте, Аноним, Вы писали:
А>ок, вот еще нужен совет :
А>потоков в пуле несколько и если на одном происходит ошибка — останавливать ли остальные и идти на останов сервиса либо 'лететь на одном крыле', т.е. пока хоть один поток пашет — логировать ошибки остальных потоков и не реагировать на них ?
что-то непонятно именно о чем вопрос, если об
1. исключениях
вообще лучше всего при возникновении такого, это красиво упасть, с минидампом и маленьким (ну можно большим) отчетиком
ненадо делать из программы живучего монстра, лучше ошибки по минидампу поправить.
2. непредвиденные ошибки сокетов, IOCP и.тд
ну тут по желанию, если что-то с сетью, то лучше все почистить, закрыть пожаловаться в лог. ну и 1 из 2, либо пытаться время от времени опять сеть слушать на порт и при удаче запустить все заново, либо закрыться.
Здравствуйте, Pepel, Вы писали:
P>люди добрые, тут ерунда какаят с этим ConnectEx() :
P>Requirements P>Client: Included in Windows XP. P>Server: Included in Windows Server 2003. P>Header: Declared in Mswsock.h. P>Library: Use Mswsock.lib
P>Ни под Win2000 ни под Vista MS Visual C++ (Studio 2003 и 2005) в упор не находят функцию, это ... это слов нет ..
P>error C3861: 'ConnectEx': identifier not found, even with argument-dependent lookup
её и все Ex IOCP функции нужно получать в рантайме.
The function pointer for the ConnectEx function must be obtained at run time by making a call to the WSAIoctl function with the SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified. The input buffer passed to the WSAIoctl function must contain WSAID_CONNECTEX, a globally unique identifier (GUID) whose value identifies the ConnectEx extension function. On success, the output returned by the WSAIoctl function contains a pointer to the ConnectEx function. The WSAID_CONNECTEX GUID is defined in the Mswsock.h header file.
это сделано специально из-за LSP драйверов, если функции экспортировать напрямую, то из-за способа их реализации о поддержке динамического изменения LSP каталога просто не может быть и речи. поэтому сделали так.
в wsock32.dll есть экспорт на AcceptEx, но где-то непомню где у микрософта написано, что напрямую ей пользоваться нельзя. потому что внутри просто тупо идет получение ее поинтера в рантайме, как в привиденном примере ниже, и только потом она вызвается, поэтому лучше сразу самому получить поинтер на функцию, так работает быстрее.
вот пример из моей проги:
#include <Mswsock.h>
LPFN_CONNECTEX m_pConnectEx = 0;
GUID guidconnectEx = WSAID_CONNECTEX;
SOCKET hSocket = CreateSocket(); // ну socket или WSASocket, просто для WSAIoctl нужен сокет :)
// skippedif(!m_pConnectEx)
{
dwT = 0;
nRet = WSAIoctl(hSocket,SIO_GET_EXTENSION_FUNCTION_POINTER,&guidConnectEx,sizeof(GUID)
,&m_pConnectEx,sizeof(m_pConnectEx),&dwT,0,0);
assert(nRet != SOCKET_ERROR);
assert(m_pConnectEx);
if(nRet == SOCKET_ERROR || !m_pConnectEx)
{
Close(hSocket);
return false;
}
}
// и все тоже самое для всех функций
// skipped
Close(hSocket);
Здравствуйте, Pepel, Вы писали:
P>да уж, c DisconnectEx() таже песня
P>варианты рассматриваю :
P>1. отказываться от поддержки Win2000
P>2. использовать connect/(shutdown+closesocket) — порнография, у меня весь остальной движок асинхронный на IOCP
P>нужен совет
ничего не поделаешь, поддежрка этих функций начинается с xp и 2003
селяви а что кто-то ещё использует w2k как серверную ос? 2003 имхо на порядок лучше, и х64 и IOCP api все есть
IOCP это серверная фишка, талмуды от микрософта говорят о кашерности написания серверов на IOCP
а AcceptEx тоже рекомендую получать в рантайме.
Здравствуйте, Pepel, Вы писали:
P>да вообщем продакшн система будет Win2003 Server, но я сижу на Win2000 Professional, поэтому вот думаю сегодня на 2003 шустренько со всеми студиями соскочить и там бодать задачу дальше
можно просто на WinXP x64, таж самая 2003 только пользовательская.
Здравствуйте, Pepel, Вы писали:
P>похож тут классическую теорию сокетов над немножко шире понимать, потому как bind() в данном случае нужен, чтобы ConnectEx() отработал, в качестве адреса передал
P>INADDR_ANY ; // любой адрес — локальный
P>отработало успешно,
P>такое вот требование у ConnectEx() значит, так ?
да сокет для ConnectEx надо сначала bind'ануть
видимо сделали для каких-то случаев чтобы конектиться с определенных интерфейсов целенаправлено игнорируя роутинг
bool IOSocket::Connect(const unsigned long uDest, const unsigned short uPort, unsigned long uBindAddr /* = INADDR_ANY */ , IOBuffer* pBuffer)
{
assert(m_pConnectEx);
assert(m_hSocket != SOCKET(INVALID_SOCKET));
if(m_hSocket == SOCKET(INVALID_SOCKET)) // INVALID_SOCKET приведен в SOCKET потому чтобы небыло проблемм при сравнении на x64return false;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = uBindAddr;
addr.sin_port = 0;
if(SOCKET_ERROR == bind(m_hSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR)))
return false;
addr.sin_addr.S_un.S_addr = uDest;
addr.sin_port = htons(uPort);
DWORD dwSent = 0;
BOOL bRet = m_pConnectEx(m_hSocket
,(const sockaddr*)&addr
,sizeof(SOCKADDR)
,pBuffer->GetBuffer(),DWORD(pBuffer->GetSize()),&dwSent,pBuffer);
int nError = WSAGetLastError();
if(!bRet && nError != ERROR_IO_PENDING)
return false;
return true;
}
Здравствуйте, Pepel, Вы писали:
P>Касатально WSASend () :
P>MSDN : The successful completion of a WSASend does not indicate that the data was successfully delivered.
P>как понимать эту тезу — delivered — о доставке кому идет речь, точнее о "НЕ ГАРАНТИИ" доставки на каком уровне говорится ? tcp это ж reliable протокол, я передал данные WSASend () — она вернула 'успех' — значит они уже в стеке сетевого интерфейса принимающей стороны, так ? об чем речь тогда ?
это не относится к IOCP, если сокет был скормлен порту завершения то тотже WSASend работает просто типа как PostQueuedCompletionStatus всегда возвращая WSA_IO_PENDING, в IOCP ошибки обрабатываются только в рабочих потоках
BOOL bRet = GetQueuedCompletionStatus(hIOPortHandle,&dwTransferred,&uCompletionKey,&pOverlapped,INFINITE);
DWORD dwRet = GetLastError();
if(bRet) dwRet = ERROR_SUCCESS;
switch(dwRet)
{
case ERROR_SUCCESS:
// do something usefullbreak;
case WAIT_TIMEOUT:
// WTF!?!?!?!?break;
default:
// :(break;
}
Здравствуйте, Pepel, Вы писали:
P>спасибо!
P>maxlosyam, здесь мне кажется тут нечто концептуальное сообщается, безотносительно к тому, асинхронный вызов отсылки данных либо синхронный, такая ж приписка есть и к функции send() .. к слову, мне сдается это вообще обозначение того, что успешное завершение функции отсылки данных в сокет (не важно где мы это завершение ловим) нифига не гарантирует их доставку ?? тоесть шлю я WSASend() (ну либо тупо send()) 200 байт, словил в первом случае на IOCP успешное завершение — это не гарантия, что на той стороне эти данные были получены, так ?
ну send то тут причем? он overlapped структурку никаким боком не кушает.
IOCP это совершенно другая песня, тупо говоря сам порт завершения уже нечто вроде "сервера" (механизм), ты ему передаешь запросы, он их выполняет и дает тебе результаты. все остальное это просто фукнции для общения с IOCP. все эти приписки относятся к blocking, non-blocking и overlapped сокетам, в случае с IOCP всю работу с сокетами берет на себя сам IOCP, это уже его проблеммы как правильно обрабатывать эти "приписки". у тебя роль уже простая, правильно давать порту завершения запросы и обрабатывать его ответы.
сценарий отправки через IOCP выглядит примерно так
1. ты через WSASend передаешь IOCP данные на отправку, если все ок WSASend возвращает SOCKET_ERROR и WSA_IO_PENDING в last error
2. IOCP делает MAGICK VOODOO VOODOO и помещает результат в очередь выполенных запросов
3. ты через GetQueuedCompletionStatus получаешь результат из этой очереди, в результате уже есть вся инфа, т.е либо отправка была ок и сколько байт 100% было отправлено, либо ошибку о том что оправка вообще обламалась.
Здравствуйте, Pepel, Вы писали:
P> к слову, мне сдается это вообще обозначение того, что успешное завершение функции отсылки данных в сокет (не важно где мы это завершение ловим) нифига не гарантирует их доставку ?? тоесть шлю я WSASend() (ну либо тупо send()) 200 байт, словил в первом случае на IOCP успешное завершение — это не гарантия, что на той стороне эти данные были получены, так ?
Именно так.
send не ждет, пока данные уйдут на удаленный конец и тот пришлет подтверждение о получении.
send тупо записывает данные в буфер отправки сетевой подсистемы (кстати размер этого буфера настраивается через вызов setsockopt).
Если в буфере отправки недостаточно места, т.е. вы пишете в буфер отправки быстрее, чем сетевая подсистема успевает их отправлять, то тогда send заблокирует (если сокет в соответсвующем режиме).
То же самое и с приемом данных из сети: сетевая подсистема их принимает и складывает в буфер приема, а вы через вызов recv их оттуда забираете.
разрабатываю сервер обработки запросов, прочитал про настоятельно рекомендуемый Рихтером метод уведомления о завершении ввода-вывода — использование IO Completion Ports, по ходу чтения возник вопрос в контексте использования IOCP применительно к сокетам и winsock в частности — я приведу свое понимание и буду признателен если мне укажут на его возможную ошибочность :
запись в очередь порта происходит после завершения асинхронной операции ввода/вывода, т.е применительно к winsock речь идет исключительно о записи/чтении данных сокета и лишь о паре функций WSASend/WSARecv — посредством этих функций мы начинаем асинхронную запись / чтение и узнаем о завершении операции посредством IOCP. Это так ? GetQueuedCompletionStatus () вернет управление только по факту завершения операций записи/чтения, начатых функциями WSASend/WSARecv на сокете привязанном к порту ? Но если так, то получается, что параллелизм начинается с момента получения данных от клиента — мой пул потоков может лишь реагировать на факт завершения вычитки из сокета данных, отправленных мне клиентом, но как распаралелить саму вычитку при помощи IOCP ?
Здравствуйте, Aspire, Вы писали:
A>Здравствуйте, Pepel, Вы писали: .. получается, что параллелизм начинается с момента получения данных от клиента — мой пул потоков может лишь реагировать на факт завершения вычитки из сокета данных, отправленных мне клиентом, но как распаралелить саму вычитку при помощи IOCP ?
A>Не понял, что значит "вычистка" и зачем тебе ее "параллелить".
не вычиСтка , а вычитка , но ничего суть в чем —
вот такой расклад положим : висит accept в ожидании — обрабатывает входящие запросы на соединение, порождая при этом некоторый набор сокетов (в количестве N), мне надо из этих сокетов естественно читать данные клиентских запросов, и я так понимаю, что всю механику (весь 'изюм') IOCP я могу использовать только с момента когда стартовал WSARecv на сокетах клиентских коннектов, но чтобы этот WSARecv запустить надо создать ведь N потоков в каждом из которых этот WSARecv и стартует. Но тогда зачем IOCP если потоков и до него как мух. Так ?
Здравствуйте, Pepel, Вы писали:
P>не вычиСтка , а вычитка
P>вот такой расклад положим : висит accept в ожидании — обрабатывает входящие запросы на соединение, порождая при этом некоторый набор сокетов (в количестве N), мне надо из этих сокетов естественно читать данные клиентских запросов, и я так понимаю, что всю механику (весь 'изюм') IOCP я могу использовать только с момента когда стартовал WSARecv на сокетах клиентских коннектов, но чтобы этот WSARecv запустить надо создать ведь N потоков в каждом из которых этот WSARecv и стартует. Но тогда зачем IOCP если потоков и до него как мух. Так ?
А что, WSARecv без потока не стартануть? Делаешь accept, затем WSARecv и забудь об этом. Вспомнишь на обработчике IOCP.
а в чем ее фишка, если не сложно в двух словах, и чем хуже вариант WSAAsyncSelect (FD_ACCEPT) + accept ?
> А что, WSARecv без потока не стартануть? Делаешь accept, затем WSARecv и забудь об этом. Вспомнишь на обработчике IOCP.
сложная конструкция, представь большой объем данных прищел, что выливается в десятки вызовов WSARecv, для каждого из которых вычитанные данные над как-то клеить и все это уже на обработчике IOCP, хотя вроде так конструкция живучая на вскидку и правдивая
Как сказал Michael Chelnokov, использовать надо функцию AcceptEx. Первая её фишка в том, что она подготавливает сокет для асинхронного подключения, т.е. о подключении клиента ты узнаешь на GetQueuedCompletionStatus в том же самом рабочем пуле, который для операций I/O. Вторая фишка — таких сокетов можно подготовить необходимое количество, зависящее от предполагаемой нагрузки. Понятно, что после приема подключения необходимо подготовить очередной сокет. Есть и третья фишка — можно при подключении клиента сразу же принять от него данные. Это надо использовать очень осторожно, да и не думай пока об этой возможности
По общей работе. На примере Echo-сервера:
GetQueuedCompletionStatus( accept ) - WSARecv : клиент подключился - постим запрос на прием
GetQueuedCompletionStatus( received ) - WSASend : приняли данные - постим запрос на передачу
GetQueuedCompletionStatus( sent ) - WSARecv : передали данные - постим запрос на прием
GetQueuedCompletionStatus( received ) - WSASend : и т.д.
...
Т.е. новый запрос постим только после завершения предыдущего. Только — значит обычно. Можно запостить сразу WSARecv и WSASend, например, при написании proxy-сервера. Можно постить WSASend без уведомления о завершении, но об этом пока не думай. Зачем может понадобиться постить несколько раз подряд WSARecv, придумать не могу
У некоторых возникает вопрос — а где тут параллелизьм и производительность? Оно в работе не с одним клиентом, но со многими.
Спасибо ! Прочитал в MSDN про AcceptEx(), сделал вывод, что в отличие от accept() это асинхронная функция — вызвали — она тут же вернула и гдет там на IOCP обработчике ловим ее завершение, но тогда непонятка — где ее вызывать повтороно ? если accept() блокирует и ее можно успешно в цикде повесить, то как быть с AcceptEx()?
> Зачем может понадобиться постить несколько раз подряд WSARecv, придумать не могу
если относительно большой объем данных клиент шлет, даже пару десятков Kb уже наверняка одним вызовом WSARecv не обойтись, или же я не догоняю и уведомление о завершении вычитки станет в IOCP толко когда все данные из сокета будут получены (положим что передаваемый нами в WSARecv буфер приемки достаточно велик) ?
Gomes, отлично, тогда правильно ли я понимаю следующее :
в такой конструкции :
первый вызов AcceptEx() должен происходить не в обработчике IOCP, а все последующие — именно в потоке обработчика IOCP
WSAAsyncSelect (FD_READ) при таком подходе не нужен, требуется лишь просто вызвать WSAAsyncSelect() для декларации перехода в асинхронный режим работы, а дальше WSARecv() гонять
тогда вопрос, а если я стартану WSARecv() вхолостую, т.е. на сокете в котором нет данных для вычитки, запись в очередь IOCP станет когда данные появятся , или же WSARecv() вернет ошибку ?
спасибо всем, проникся действительно winsock+IOCP это могучий подход, пока архитектуру рисую — вот по ходу еще вопрос возник — стартанул я WSARecv()на некотором клиентском сокете и пошел себе дальше, но представим, что клиент на данном сокете закрыл соединение (пусть даже не отправляя мне ничего вообще), — в таком случае в очередь завершения IOCP станет запись с кодом ошибки на сокете , так ?
есть IOCP и есть привязанный к нему сокет MYSOCK, на котором последовательно стартовал серию асинхронных вызовов : ConnectEx, WSARecv, WSASend, дальше на обработчике IOCP ловлю завершение стартованных мной операций,
вопрос в механизме получения мной информации о том, какой из вызовов завершился, понял, что наиболее подходящий вариант да и похоже единственный (!) — параметр CompletionKey функции CreateIoCompletionPort() его тип ULONG_PTR что позволяет передавать даже адрес объекта, но вот народ что именно туда лепит ? какие идентификаторы ? структура OVERLAPPED тоже обилием пространтсва для таких маневров не блещет — только поле HANDLE hEvent, что мало да и c типом HANDLE над обращаться осторожно насколько понимаю. К тому же если сокетов привязоно к IOCP во множественном количестве — на каком из них событие произошло ? Пока, единственное, что пришло на ум — в CompletionKey при привязке сокета к IOCP передавать хэндл сокета либо адрес некой собственной конструкции(структуры), а в OVERLAPPED.hEvent — код операции (закодировать целочисленным набором основные вызовы свои : ConnectEx — 0, WSARecv — 1, .. ).
Поправьте меня, если я отклонился от курса в понимании .
Вот толком не понял до конца — создал я сокет, далее мне требуется его дискриптор использовать в :
1. CreateIoCompletionPort для привязки к IOCP
2. последующих асинхронных вызовах
MSDN — CreateIoCompletionPort — "After an instance of an open file is associated with an I/O completion port, it cannot be used in the ReadFileEx or WriteFileEx function. It is best not to share such an associated file through either handle inheritance or a call to the DuplicateHandle function. Operations performed with such duplicate handles generate completion notifications."
я правильно понимаю — нужно обязательно плодить дубликат дискриптора сокета, для того чтобы разруливать 1.и 2. ? т.е. в асинхронных вызовах использовать именно копию дискриптора сокета , так ?
Спасибо всем огромное, постигаю потиху ..изящная механика конечно, те, кто кидает какашки в Окна очевидно про winsock & iocp не слышали, у меня вопрос еще такой :
стартую WSARecv — на IOCP ловлю количество принятых, я могу столкнуться с ситуацией "буфер мал" и если да, то где — на IOCP или сразу на вызове WSARecv ?
по поводу WSASend — если я передал туда буфер с 500 байтами к примеру, а на IOCP ловлю запись, что передано всего 300 — такое может быть ? я так понимаю в любом случае буфер-передатчик после старта WSASend надо держать и не трогать до тех пор, пока на IOCP не словлю завершение отправки всех его данных — это так ?
Салют друзья-коллеги, тут походу вопросов у меня насобиралось :
1. Дескриптор сокета — я так понимаю безотносительно к конструкциям на которые он дает привязку это обычное число unsigned long и, получив его при создании сокета (WSASocket()), могу в рамках процесса смело гонять из функции в функцию до тех пор пока closesocket() на дескрипторе не отработаю, а ф-ция DuplicateHandle() — для межпроцессовой передачи доступа к объектам и нафиг в рамках одного процесса не нужна.
2. Вызов listen(..) — второй параметр задает размер очереди для клиентов на ожидание подключения, смотрел пару примеров народ туда 1 лепит, а почему не SOMAXCONN константу ? — ведь в этом случае согласно хэлпу размер данной очереди будет == вменяемому максимуму. Я так понимаю стартовал я AcceptEx() — словил на IOCP его завершение и тут ж надо как можно быстрее опять стартовать AcceptEx() — а все клиенты которые в этот промежуток будут цепляться станут в очередь — если размерность очереди это позволит, т.е. в listen над погуще лепить второй параметр, никак не 1.
3. Выяснение удачи-неудачи завершения асинхронной операции — я так понял анализа возврата GetQueuedCompletionStatus() предостаточно, вызов WSAGetOverlappedResult() продублирует данную информацию, ничего нового не даст и по сути не нужен.
4. Поведение в случае сбоя :
сбойнул AcceptEx() — не важно где просек — сразу на старте или на завершении в IOCP обработчике — я так понимаю сервис надо стопорить по любому, потому как дальше бодаться уже дорого и успех не гарантирован ;
сбойнули WSASend()/WSARecv() — не важно где просек — сразу на старте или на завершении в IOCP обработчике — можно грохнуть клиентский сокет и забыть про него — сервис пусть продолжает работать ;
Здравствуйте, Gomes, Вы писали:
G>Здравствуйте, Pepel, Вы писали:
P>>Я так понимаю стартовал я AcceptEx() — словил на IOCP его завершение и тут ж надо как можно быстрее опять стартовать AcceptEx() G>Так. Только предварительно лучше стартовать несколько AcceptEx. Сколько — зависит от предполагаемой нагрузки.
А точнее лучше столько AcceptEx сколько рабочих потоков, т.е по AcceptEx'у на поток больше смысла нету, быстрее не будет.
ну а рабочих потоков лучше столько сколько процессоров (ядер), имхо так оптимально быстро получается.
Спасибо всем, вот на практике уперся в то, что поскольку возможна ситуация, когда WSASend() может отправить только часть данных, то мало толку выходит от его вызова в асинхронном режиме — все равно вызывай его пока все не затолкаешь — а если между такими асинхронными заталкиваниями в очередь IOCP станет запись от других обработчиков на передачу данных в этот же сокет. Я понимаю , что ситуация частичной отправки редкая, но по хорошему ее следует учитывать и выстраивать конструктив обработки с учетом множественного вызова WSASend для передачи одного буфера. Тогда не проще ли синхронно в цикле толкать этим же WSASend пока все данные буфера не залетят в сокет ? Тоесть читаем асинхронно с ожиданием завершения на IOCP , а писать все таки в сокет синхронно. Поправьте если что-то не догоняю.
на порт прослушки моего сервис цепляются клиенты, я должен предобработать их запросы и передать другому удаленному сервису S — от него получить подтверждение о приеме и назад разогнать клиентам. мой подход с Вашей помощью : я стартую на клиентах асинхронно вычитку (WSARecv() для каждого) и словив ее завершение на IOCP (4 потока в пуле на 2процессорной системе) для некого клиента CLi — предобрабатываю пакет — и теперь должен передать его в сокет завязанный на удаленный целевой сервис S. поскольку в пуле потоков потоков более одного — я закрыл доступ к сокету целевого сервиса S критической секцией — ура, НО ! наставив асинхронных WSASend (топчу предобработанные данные от клиентов в сокет целевого сервиса S) в очередь IOCP я с чем сталкиваюсь на IOCP обработчике :
WSASend1 — целиком отправил буфер — от клиента A
WSASend2 — ЧАСТИЧНО !! (300 байт из 500) — от клиента B
WSASend3 — целиком отправил буфер — от клиента C
WSASend4 — целиком отправил буфер — от клиента D
на стороне целевого сервиса S это не проканает — там протокол прикладной и он ждет все пакеты в потоке целиком, там заголовки — в них длины пакетов и проче. Т.о. поскольку WSASend может отправить лишь часть пачки (это реалии — тут все понятно), то асинхронность в моей ситуации не союзник выходит . Я про это. Или я чет не догоняю.
>Только недавно писал, что постиг всю красоту, и тут такое
постиг че слово !! тока взаимность дается трудами .. IOCP ему ж цветы не подаришь и в рестор не сводишь.
Здравствуйте, Pepel, Вы писали:
P>на стороне целевого сервиса S это не проканает — там протокол прикладной и он ждет все пакеты в потоке целиком, там заголовки — в них длины пакетов и проче. Т.о. поскольку WSASend может отправить лишь часть пачки (это реалии — тут все понятно), то асинхронность в моей ситуации не союзник выходит .
Не понимаю. Ну отправь оставшиеся 200 байт после завершения 300. В чем проблема?
Здравствуйте, Gomes
G>Не понимаю. Ну отправь оставшиеся 200 байт после завершения 300. В чем проблема?
словил я завершение частичной вычитки WSASend — ЧАСТИЧНО (300 байт из 500) на одном из обработчиков IOCP — а обработчиков у меня боле одного — пока я кумекаю и леплю запрос на вычитку остатка — в это время любом другом параллельном обработчике (4 потока в пуле) тихо и мирно по факту завершения предобработки данных другого клиента вызывается WSASend () и этот вызов вклинивается между моими двумя WSASend (первые 300 байт) и WSASend (оставшиеся 200 байт), т.е. засада в том, что вычитанные данные от многих клиентов мне надо писать в один целевой сокет и частичная запись тут порет весь натюрморт
да, друзья спасибо огромное, именно так и есть — сервер удаленный этот он и один коннект только дает,
perf13, да, суть в этом, я уже каркас такой и заложил с очередями-контейнерами для накопления данных от/для сокета , вот действительно асинхронность куречить синхронными вставками не хочется, осмысливаю сейчас Ваш совет — как он ложится
да, вообщем, все по делу, действительно в моем случае все равно по любому требуется реализовать "один вызов WSASend() — одно завершение" и помогает удержать эту 'линейку' промежуточная очередь-контейнер данных для отправки в сокет
Здравствуйте, maxlosyam, Вы писали:
M>Здравствуйте, Pepel, Вы писали:
P>>да, вообщем, все по делу, действительно в моем случае все равно по любому требуется реализовать "один вызов WSASend() — одно завершение" и помогает удержать эту 'линейку' промежуточная очередь-контейнер данных для отправки в сокет
M>рекомендую использовать TransmitPackets, он пошустрее M>ну а вообще пакеты в твоем случае надо фреймовать, т.е удаленном серверу передаешь не просто пакеты друг за другом, а каждый пакет вкладываешь во фрейм с инфой о том чей это пакет и какой он длинны, и на удаленном уже пакеты из фремов извлекаешь как хочешь. M>но если удаленный сервер изменить нельзя, то фигово
упс, пародон, хню сказал.
да, если в один сокет, то полюбому пакеты четко друг за другом должны быть.
по ходу возник вопрос : интересует аналитика сбоев при работе с удаленными хостами будь то клиенты либо сервера,
для сокетов моего приложения, соединенных с удаленным клиентом (AcceptEx()), и сокетов, соединенных с удаленным сервером (ConnectEx()), хотелось бы отслеживать ситуацию неуспешного завершения вызова, означающее, что другая сторона просто недоступна, т.е. "я все делаю правильно, мои вызовы верны, но тот берег недоступен" и тогда для сокета соединенного с клиентском — сворачивать его и работать дальше, для сокета соединенного с сервером — по таймауту пытаться восстановить соединение
посмотрел Windows Sockets Error Codes, вот похоже подмножество кодов, означающее то, что мне интересно :
WSAENETDOWN Network is down.
WSAENETUNREACH Network is unreachable.
WSAENETRESET Network dropped connection on reset.
WSAECONNABORTED Software caused connection abort.
WSAECONNRESET Connection reset by peer.
WSAETIMEDOUT Connection timed out.
WSAECONNREFUSED Connection refused.
WSAEHOSTDOWN Host is down.
WSAEHOSTUNREACH No route to host.
WSAEDISCON Graceful shutdown in progress.
но это много, на какие именно возвраты стоит ориентироваться ?
Re[24]: winsock и порты завершения ввода-вывода
От:
Аноним
Дата:
13.04.09 12:45
Оценка:
ок, вот еще нужен совет :
потоков в пуле несколько и если на одном происходит ошибка — останавливать ли остальные и идти на останов сервиса либо 'лететь на одном крыле', т.е. пока хоть один поток пашет — логировать ошибки остальных потоков и не реагировать на них ?
Re[26]: winsock и порты завершения ввода-вывода
От:
Аноним
Дата:
13.04.09 15:42
Оценка:
ок, спасибо
>не надо делать из программы живучего монстра
да, вообщем грамотное предложение, подумал вот, действительно лучше свернуть сервис после ЛЮБОГО сбоя, потому как тяжело гарантированно установить класс сбоя и процесс после такого рода 'умных' анализаторов может долго 'работать' на самом деле не работая — а это дичь которая похуже остановов/рестартов
Здравствуйте, maxlosyam, Вы писали:
M>Здравствуйте, Pepel, Вы писали:
P>>люди добрые, тут ерунда какаят с этим ConnectEx() :
P>>Requirements P>>Client: Included in Windows XP. P>>Server: Included in Windows Server 2003. P>>Header: Declared in Mswsock.h. P>>Library: Use Mswsock.lib
P>>Ни под Win2000 ни под Vista MS Visual C++ (Studio 2003 и 2005) в упор не находят функцию, это ... это слов нет ..
P>>error C3861: 'ConnectEx': identifier not found, even with argument-dependent lookup
M>её и все Ex IOCP функции нужно получать в рантайме.
maxlosyam, вопрос тогда : ладно, AcceptEx я уже вяжу статически через lib, по Вашему совету ConnectEx и DisconnectEx — получу их точки входа в рантайме приведенным способом, но как быть с поддержкой Win2000 ? т.е. на ней я не смогу получить точки входа этих функций ? похоже нет, т.е. отказ от поддержки приложением это версии ОС, так ? Вы как делаете в таком случае ?
да вообщем продакшн система будет Win2003 Server, но я сижу на Win2000 Professional, поэтому вот думаю сегодня на 2003 шустренько со всеми студиями соскочить и там бодать задачу дальше
похож тут классическую теорию сокетов над немножко шире понимать, потому как bind() в данном случае нужен, чтобы ConnectEx() отработал, в качестве адреса передал
MSDN : The successful completion of a WSASend does not indicate that the data was successfully delivered.
как понимать эту тезу — delivered — о доставке кому идет речь, точнее о "НЕ ГАРАНТИИ" доставки на каком уровне говорится ? tcp это ж reliable протокол, я передал данные WSASend () — она вернула 'успех' — значит они уже в стеке сетевого интерфейса принимающей стороны, так ? об чем речь тогда ?
maxlosyam, здесь мне кажется тут нечто концептуальное сообщается, безотносительно к тому, асинхронный вызов отсылки данных либо синхронный, такая ж приписка есть и к функции send() .. к слову, мне сдается это вообще обозначение того, что успешное завершение функции отсылки данных в сокет (не важно где мы это завершение ловим) нифига не гарантирует их доставку ?? тоесть шлю я WSASend() (ну либо тупо send()) 200 байт, словил в первом случае на IOCP успешное завершение — это не гарантия, что на той стороне эти данные были получены, так ?