Есть вопрос по работе с сокетом через порты завершения ввода/вывода

Тема в разделе "WASM.NETWORKS", создана пользователем s3dworld, 26 сен 2011.

  1. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Всем доброго дня!

    Создаю асинхронный сокет:

    Код (Text):
    1. SOCKET client=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED);
    2. if(client==INVALID_SOCKET) return error;
    Создаю порт и связываю с сокетом:

    Код (Text):
    1. HANDLE port=CreateIoCompletionPort(client,0,0,1);
    2. if(!port) return error;
    Предположим я уже подключился через одну из трёх функций и вот уже хочу отправить данные. Для этого я буду использовать WSASend():

    Код (Text):
    1. int result=0;
    2. char buffer[32];
    3. // Заполняю буфер данными
    4. WSABUF bufferSend;
    5. bufferSend.buf=buffer;
    6. bufferSend.len=32;
    7. DWORD sendBytes=0;
    8. MYOVERLAPPED overlapped; // Унаследованная от WSAOVERLAPPED
    9. overlapped.type=TYPE_SEND;
    10. result=WSASend(client,&bufferSend,1,&sendBytes,0,&overlapped,0);
    11. // Проверка значения в result
    Что же тут может произойти? Во-первых, может возникнуть ошибка (сеть недоступна) или штатная ситуация, когда соединение было завершено (например завершено удалённой стороной). Во-вторых, возможны ситуации, когда функция тут же передаст управление (я полагаю именно в данной ситуации sendBytes вернёт столько, сколько нам нужно было передать и ни на байт меньше, я прав?). В-третьих: если операция не может быть выполнена немедленно, то функция вернёт нам WSA_IO_PENDING. Да, оговорюсь, не функция вернёт, а код ошибки вернёт, при возвращении функцией значения SOCKET_ERROR. Но вот не понятно мне в каком случае может вернуться WSA_OPERATION_ABORTED. Мы же только запустили функцию, она должна мгновенно передать управление. Это что, если вдруг за это мгновение я из другого потока отменил перекрёстную (асинхронную) операцию, то вот это и произойдёт? Что-то мне кажется маловероятным возникновение такой ситуации. Или я ошибаюсь?

    Но больше всего меня интересует следующее, если мы получили WSA_IO_PENDING, то значит ли это, что при ожидании завершения:

    Код (Text):
    1. bool isExit=false;
    2. DWORD numberBytes=0;
    3. PULONG_PTR key=0;
    4. MYOVERLAPPED overlapped;
    5.  
    6. while(!isExit)
    7. {
    8.     GetQueuedCompletionStatus(port,&numberBytes,&key,&overlapped,INFINITY);
    9.     // Обработка
    10. }
    В numberBytes всегда будет такое значение, сколько мы хотели отправить? Просто в противном случае было бы больше кода отслеживания. Хотя я сам уже думаю что всё таки получить тут я могу разное число, но могу ли я получить тут 0 и в каких случаях?

    Мне не понятно поведение системы в такой вот ситуации: я вызываю WSASend() и прошу её передать 75 МБ (размер выбрал для наглядности примера). Соединение с сокетом существует, операция не может быть выполнена мгновенно, в результате я получу ошибку WSA_IO_PENDING (собственно это успешная ошибка). Я занимаюсь своими делами, а система за меня отсылает данные (видимо тут я только должен позаботиться чтобы буфер с отсылаемыми данными не накрылся при выходе из области видимости, в которой вызывалась WSASend(), то есть сделать его не на стеке как в приведённом выше примере). Через 40 секунд сервер (а может и я сам) завершает соединение со мной, но я успел передать только 13 МБ. Функция GetQueuedCompletionStatus() передаст управление. И вопрос к Вам, что она мне вернёт?
     
  2. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    s3dworld
    Если WSASend возвратила не SOCKET_ERROR, то функция отработала сразу (очередь IOCP не была заюзана).
    Если WSASend вернула SOCKET_ERROR, то:
    проверяем результат вызова WSAGetLastError:
    1) WSA_IO_PENDING - IOCP поставил наш запрос в очередь (будем ждать ответа из GetQueuedCompletionStatus)
    2) WSAEWOULDBLOCK - в данный момент система не может выполнить запрошенную операцию с этим экземпляром сокета (необходимо попробовать повторить её позже)
    3) При любой другой ошибке закрываем сокет (closesocket)

    Функция WSASend может возвратить в lpNumberOfBytesTransferred (параметр ф-ии GetQueuedCompletionStatus) число меньшее чем суммарный размер буферов переданных через параметр LPWSABUF lpBuffers. И это будет означать, что на часть данных от корреспондента получена квитанция, после чего связь была потеряна. Однако на практике такого варианта не наблюдалось.

    Если GetQueuedCompletionStatus (для нашего WSASend и Overlapped) вернула:
    1) TRUE:
    if (lpNumberOfBytesTransferred == 0) closesocket(sock); // т.к. произошёл дисконнект
    2) FALSE:
    closesocket(sock);

    ЗЫ. Вообще лучше на такие вопросы ответят на RSDN.
     
  3. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    T800
    Спасибо!

    А например узнать причину отключения как можно? Код будет в GetLastError() или же GetQueuedCompletionStatus() в какой-то из параметров его положит? Я про такие ошибки, как:

    - WSAECONNABORTED
    - WSAECONNRESET
    - WSAENETDOWN
    - WSAENETRESET
    - WSA_OPERATION_ABORTED
     
  4. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    s3dworld
    Причину отключения не знаю как узнать, т.к. нет такой задачи у моего сервака.
    А вот когда проверять на "ошибки" скажу:
    Если (SOCKET_ERROR + WSA_IO_PENDING), то проверяем после выхода из GetQueuedCompletionStatus (при FALSE вызываем GetLastError).

    ЗЫ. Полагаю, что большая часть перечисленных "ошибок" на винде просто не возникает. Особенно при юзании IOCP.
     
  5. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    T800
    > ЗЫ. Полагаю, что большая часть перечисленных "ошибок" на винде просто не возникает. Особенно при юзании IOCP.
    Ну например: подключился ко мне клиент. Сказал асинхронно отправить 10 МБ данных. Жду завершения. Клиент не получив всех данных вызывает closesocket() (либо у него свет отключили, да и ради примера я вызову closesocket() этого клиента). Разве в этом случае не произойдут ошибки, перечисленные выше? То есть WSAENETRESET, WSAECONNABORTED и WSAECONNRESET (да и WSAETIMEOUT) должны быть. Или я не прав?
     
  6. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    s3dworld
    Тут надо отдельно 2 варианта рассмотреть.
    1) Отключили свет.
    Полагаю GetQueuedCompletionStatus возвратит FALSE, а GetLastError вернёт какую то из перечисленных ошибок (полагаю что WSAENETDOWN).
    2) Клиент послал FIN на сервак.
    Полагаю GetQueuedCompletionStatus возвратит FALSE, а GetLastError вернёт какую то из перечисленных ошибок.

    Это надо проверять. Только я всегда в таких случаях закрываю сокет и не мучаюсь, т.к. не критичная ситуация.
     
  7. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    T800
    > 2) Клиент послал FIN на сервак.
    > Полагаю GetQueuedCompletionStatus возвратит FALSE, а GetLastError вернёт какую то из перечисленных ошибок.
    А что за FIN?
     
  8. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    s3dworld
    http://ru.wikipedia.org/wiki/TCP
     
  9. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    T800
    Ага, ясно. А то я подумал про другое...

    А в каком случае у WSASend() может возникнуть WSA_OPERATION_ABORTED?
     
  10. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    s3dworld
    Полагаю тогда, когда IOCP сервак не верно спроектирован.

    Можно представить такую ситуацию ...
    Ты вызваешь WSASend не строго после вызова WSARecv (вариант запрос - ответ, HTTP), а можешь вызывать WSASend в любой момент (сервак хочет клиенту кинуть ну очень важные данные и срочно).
    В такой ситуации возможен вызов двух WSASend (для одного сокета, но для разных Overlapped !!!).
    При обработке первого WSASend (точнее первого Overlapped) мы закрываем сокет. Для второго WSASend думаю стоит ожидать WSA_OPERATION_ABORTED.

    Но это не корректный алгоритм работы! Надо избегать "одновременного" вызова WSASend. А если это требуется, то делать свою очередь для отправки данных.
     
  11. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    T800
    Спасибо за разъяснения))
     
  12. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    А мне вот интересен такой акспект - как поведет себя асинхронная отправка в том случае, когда удаленная сторона подключена на очень медленной скорости?

    Скажем, хочет сервер отправить мегабайт 10 клиенту, подключившемуся по DialUp (а сам сервер, предположим, подключен к 100 мегабиному порту свитча и далее оптика в инет). Сразу одним скопом все 10 мегабайт сервер (комп) не сможет зафигачить клиенту - после 10-20 килобайт отправка упрется в неподтвержденное TCP-окно. То есть, TCP-IP стек должен будет где-то все хранить, то есть в буферах. Но буфера не очень резиновые, и явно просто одной командой сокет откажется принять огромный объем данных. То есть, приложение должно будет отправлять порциями разумного размера (скажем, по нескольку килобайт). При этом все то, что TCP-стек не смог еще отправить из-за TCP-окна, должно накапливаться в буферах TCP-стека. В один прекрасный момент TCP-стек должен решить, что буфер вот-вот переполнится, и должен отказать приложению в очереном сенде. Я с такой ситуацией лично не встречался, потому что сервер работал с очень маленькими порциями информации. Но если так, как я написал, то как будет выглядеть этот отказ? WSAEWOULDBLOCK?
     
  13. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    Dmitry_Milk
    На RSDN как раз таки и говорят, что при использовании WSASend событие WSAEWOULDBLOCK возможно, когда два WSASend вызываются друг за дружкой с большими буферами (10МБ + 10МБ). Первый вызов WSASend должен выполнится, а на втором должны получить WSAEWOULDBLOCK.
    Но этот случай, вроде, не касается overlapped режима. Хотя ...
     
  14. Smile

    Smile New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2004
    Сообщения:
    129
    А есть еще такие функции, не пользовался но по описанию подходит

    TransmitFile TransmitPackets
    http://msdn.microsoft.com/en-us/library/ms740565(v=VS.85).aspx