Есть вопрос по работе с сокетом через порты завершения ввода
От: s3dworld Россия  
Дата: 26.09.11 12:32
Оценка:
Всем доброго дня!

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

SOCKET client=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED);
if(client==INVALID_SOCKET) return error;


Создаю порт и связываю с сокетом:

HANDLE port=CreateIoCompletionPort(client,0,0,1);
if(!port) return error;


Предположим я уже подключился через одну из трёх функций и вот уже хочу отправить данные. Для этого я буду использовать WSASend():

int result=0;
char buffer[32];
// Заполняю буфер данными
WSABUF bufferSend;
bufferSend.buf=buffer;
bufferSend.len=32;
DWORD sendBytes=0;
MYOVERLAPPED overlapped; // Унаследованная от WSAOVERLAPPED
overlapped.type=TYPE_SEND;
result=WSASend(client,&bufferSend,1,&sendBytes,0,&overlapped,0);
// Проверка значения в result


Что же тут может произойти? Во-первых, может возникнуть ошибка (сеть недоступна) или штатная ситуация, когда соединение было завершено (например завершено удалённой стороной). Во-вторых, возможны ситуации, когда функция тут же передаст управление (я полагаю именно в данной ситуации sendBytes вернёт столько, сколько нам нужно было передать и ни на байт меньше, я прав?). В-третьих: если операция не может быть выполнена немедленно, то функция вернёт нам WSA_IO_PENDING. Да, оговорюсь, не функция вернёт, а код ошибки вернёт, при возвращении функцией значения SOCKET_ERROR. Но вот не понятно мне в каком случае может вернуться WSA_OPERATION_ABORTED. Мы же только запустили функцию, она должна мгновенно передать управление. Это что, если вдруг за это мгновение я из другого потока отменил перекрёстную (асинхронную) операцию, то вот это и произойдёт? Что-то мне кажется маловероятным возникновение такой ситуации. Или я ошибаюсь?

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

bool isExit=false;
DWORD numberBytes=0;
PULONG_PTR key=0;
MYOVERLAPPED overlapped;

while(!isExit)
{
    GetQueuedCompletionStatus(port,&numberBytes,&key,&overlapped,INFINITY);
    // Обработка
}


В numberBytes всегда будет такое значение, сколько мы хотели отправить? Просто в противном случае было бы больше кода отслеживания. Хотя я сам уже думаю что всё таки получить тут я могу разное число, но могу ли я получить тут 0 и в каких случаях?

Мне не понятно поведение системы в такой вот ситуации: я вызываю WSASend() и прошу её передать 75 МБ (размер выбрал для наглядности примера). Соединение с сокетом существует, операция не может быть выполнена мгновенно, в результате я получу ошибку WSA_IO_PENDING (собственно это успешная ошибка). Я занимаюсь своими делами, а система за меня отсылает данные (видимо тут я только должен позаботиться чтобы буфер с отсылаемыми данными не накрылся при выходе из области видимости, в которой вызывалась WSASend(), то есть сделать его не на стеке как в приведённом выше примере). Через 40 секунд сервер (а может и я сам) завершает соединение со мной, но я успел передать только 13 МБ. Функция GetQueuedCompletionStatus() передаст управление. И вопрос к Вам, что она мне вернёт?
Re: Есть вопрос по работе с сокетом через порты завершения в
От: 11molniev  
Дата: 26.09.11 13:02
Оценка:
Здравствуйте, s3dworld, Вы писали:
.....
S>Мне не понятно поведение системы в такой вот ситуации: я вызываю WSASend() и прошу её передать 75 МБ (размер выбрал для наглядности примера). Соединение с сокетом существует, операция не может быть выполнена мгновенно, в результате я получу ошибку WSA_IO_PENDING (собственно это успешная ошибка). Я занимаюсь своими делами, а система за меня отсылает данные (видимо тут я только должен позаботиться чтобы буфер с отсылаемыми данными не накрылся при выходе из области видимости, в которой вызывалась WSASend(), то есть сделать его не на стеке как в приведённом выше примере). Через 40 секунд сервер (а может и я сам) завершает соединение со мной, но я успел передать только 13 МБ. Функция GetQueuedCompletionStatus() передаст управление. И вопрос к Вам, что она мне вернёт?

Скока передаст столько и вернет. Тоже относиться и к синхронному выполнению WSASend.
Re: Есть вопрос по работе с сокетом через порты завершения в
От: Michael Chelnokov Украина  
Дата: 26.09.11 13:04
Оценка:
Здравствуйте, s3dworld, Вы писали:

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


В теории — что угодно (ошибку или число от 1 до 75M).
На практике — 75M если оно поместилось в буфер ядра (т.е. вызов завершился до того как стало известно о разрыве соединения), или ошибку.
Я как-то недавно экспериментировал с различными большими кусками данных, и ни разу не удалось получить иного результата.
Re[2]: Есть вопрос по работе с сокетом через порты завершени
От: s3dworld Россия  
Дата: 26.09.11 13:10
Оценка:
Здравствуйте, 11molniev, Вы писали:

1>Здравствуйте, s3dworld, Вы писали:

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

1>Скока передаст столько и вернет. Тоже относиться и к синхронному выполнению WSASend.

То есть получается, что когда я хочу передать 7 МБ, на порт завершения может прийти уведомление,где в параметре lpNumberOfBytes я получу 3 МБ и мне нужно снова вызвать WSASend() чтобы отправить не отправленные 4 МБ (не подумайте что я буду отправлять заново весь буфер, только то, что не отправилось)? Вообще странное поведение для асинхронной функции. Я то думал что если я хочу отправить 7 МБ, то она хоть в лоб разобъётся, но в порт отправится пакет либо если все 7 МБ передаст, либо если произойдёт ошибка. А тот через задницу.

А если в момент асинхронной операции произойдёт отключение сокета, где я могу получить код ошибки после получения управления от GetQueuedCompletionStatus()? Просто вызвав GetLastError()?
Re[3]: Есть вопрос по работе с сокетом через порты завершени
От: acDev Россия  
Дата: 26.09.11 14:39
Оценка:
Здравствуйте, s3dworld, Вы писали:

S>То есть получается, что когда я хочу передать 7 МБ, на порт завершения может прийти уведомление,где в параметре lpNumberOfBytes я получу 3 МБ и мне нужно снова вызвать WSASend() чтобы отправить не отправленные 4 МБ (не подумайте что я буду отправлять заново весь буфер, только то, что не отправилось)?


На практике параметр lpNumberOfBytes возвратит либо 0, либо 7 МБ !!!
Другое означает сбой винды.

S>А если в момент асинхронной операции произойдёт отключение сокета, где я могу получить код ошибки после получения управления от GetQueuedCompletionStatus()? Просто вызвав GetLastError()?


Верно (если WSASend вернула WSA_IO_PENDING).
Re[4]: Есть вопрос по работе с сокетом через порты завершени
От: s3dworld Россия  
Дата: 26.09.11 14:43
Оценка:
Здравствуйте, acDev, Вы писали:

D>Здравствуйте, s3dworld, Вы писали:


S>>То есть получается, что когда я хочу передать 7 МБ, на порт завершения может прийти уведомление,где в параметре lpNumberOfBytes я получу 3 МБ и мне нужно снова вызвать WSASend() чтобы отправить не отправленные 4 МБ (не подумайте что я буду отправлять заново весь буфер, только то, что не отправилось)?


D>На практике параметр lpNumberOfBytes возвратит либо 0, либо 7 МБ !!!

D>Другое означает сбой винды.

S>>А если в момент асинхронной операции произойдёт отключение сокета, где я могу получить код ошибки после получения управления от GetQueuedCompletionStatus()? Просто вызвав GetLastError()?


D>Верно (если WSASend вернула WSA_IO_PENDING).

Спасибо!

А у меня вот такой вопрос. Стоит ли создавать несколько портов завершения для обработки тысячи сокетов или же лучше всех их привязать к одному порту?
Re[5]: Есть вопрос по работе с сокетом через порты завершени
От: acDev Россия  
Дата: 26.09.11 15:06
Оценка:
Здравствуйте, s3dworld, Вы писали:

S>А у меня вот такой вопрос. Стоит ли создавать несколько портов завершения для обработки тысячи сокетов или же лучше всех их привязать к одному порту?


Не стоит, т.к. это только нагрузит ядро винды лишней работой.
Эта задача разруливается правильной организацией пула потоков IOCP и количеством потоков в пуле.
Re: Есть вопрос по работе с сокетом через порты завершения в
От: DenRaskatov  
Дата: 18.10.11 07:57
Оценка:
Здравствуйте, s3dworld, Вы писали:

S>Мне не понятно поведение системы в такой вот ситуации: я вызываю WSASend() и прошу её передать 75 МБ ... Функция GetQueuedCompletionStatus() передаст управление. И вопрос к Вам, что она мне вернёт?

Думаю, что в ответ на WSASend функция GetQueuedCompletionStatus() вернет либо 0 (в случае если операция не была выполнена до конца) либо 75Мб (в случае если операция была выполнена до конца). Никогда не сталкивался с тем, чтобы GetQueuedCompletionStatus() возвращала, например, "34 Мб переданы успешно, а остальные нет".

У меня "смежная" с темой проблема, помогите, пожалуйста разобраться.
Вызываю WSASend() и прошу передать 75Мб. Функция WSASend() возвращает результат 0, что означает, что операция выполнена, вызов GetQueuedCompletionStatus() также сообщает мне, что операция завершена и все данные отправлены полностью. В действительности данные еще никуда не передавались, а ОС просто скопировала их в свои внутренние буферы (уж не знаю куда) и только начала их передачу. Если теперь при передаче этих 75 Мб произойдет ошибка и данные не будут отправлены полностью, как я об этом узнаю?
Re[2]: Есть вопрос по работе с сокетом через порты завершени
От: acDev Россия  
Дата: 18.10.11 08:57
Оценка:
Здравствуйте, DenRaskatov, Вы писали:

DR>Вызываю WSASend() и прошу передать 75Мб. Функция WSASend() возвращает результат 0, что означает, что операция выполнена, вызов GetQueuedCompletionStatus() также сообщает мне, что операция завершена и все данные отправлены полностью. В действительности данные еще никуда не передавались, а ОС просто скопировала их в свои внутренние буферы (уж не знаю куда) и только начала их передачу.


При таком большом буфере вы не получите 0 от WSASend, вы получите SOCKET_ERROR. И будете вынуждены "ждать ошибки" на GetQueuedCompletionStatus().
Re[3]: Есть вопрос по работе с сокетом через порты завершени
От: DenRaskatov  
Дата: 18.10.11 09:07
Оценка:
Здравствуйте, acDev, Вы писали:

D>При таком большом буфере вы не получите 0 от WSASend, вы получите SOCKET_ERROR. И будете вынуждены "ждать ошибки" на GetQueuedCompletionStatus().


Вы правы, действительно от WSASend() получаю SOCKET_ERROR. Однако, из GetQueuedCompletionStatus() возвращаюсь до того, как все данные переданы.
Возможно я что-то путаю: GetQueuedCompletionStatus() так и должна работать, сообщая, что данные именно отправлены, а не получены. С другой стороны, протокол TCP является надежным и на каждый переданный пакет получает подтверждение от принимающей стороны. Почему тогда функция GetQueuedCompletionStatus() не устроена так, чтобы я получал уведомление о завершении операции отправки, только когда все данные доставлены и подтверждены?
Может надо какую-то опцию на сокете установить?
Re[3]: Есть вопрос по работе с сокетом через порты завершени
От: DenRaskatov  
Дата: 18.10.11 09:15
Оценка:
Здравствуйте, acDev, Вы писали:

D>При таком большом буфере вы не получите 0 от WSASend, вы получите SOCKET_ERROR. И будете вынуждены "ждать ошибки" на GetQueuedCompletionStatus().

Если опцию SO_SNDBUF установить равную 0, то от WSASend получаю SOCKET_ERROR.
Если опцию SO_SNDBUF установить не равной 0, то от WSASend получаю 0.
Re[4]: Есть вопрос по работе с сокетом через порты завершени
От: acDev Россия  
Дата: 18.10.11 09:29
Оценка:
Здравствуйте, DenRaskatov, Вы писали:

DR>Вы правы, действительно от WSASend() получаю SOCKET_ERROR. Однако, из GetQueuedCompletionStatus() возвращаюсь до того, как все данные переданы.

Вы даёте мало инфы. После SOCKET_ERROR (WSASend) напишите что возвращает WSAGetLastError(). Также напишите что возвращает GetQueuedCompletionStatus() и WSAGetLastError().

DR>Если опцию SO_SNDBUF установить равную 0, то от WSASend получаю SOCKET_ERROR.

DR>Если опцию SO_SNDBUF установить не равной 0, то от WSASend получаю 0.
Опять таки судить только по SOCKET_ERROR не могу. Проверяйте WSAGetLastError().
И напишите буфер какого размера выставляете и сколько передаёте.
Re[5]: Есть вопрос по работе с сокетом через порты завершени
От: DenRaskatov  
Дата: 18.10.11 10:07
Оценка:
Здравствуйте, acDev.
Попробую привести код, который использую.

Кратко:
    wsaSendingBuffer.buf = (char*) malloc(50000000);
    wsaSendingBuffer.len = 50000000;

    dwBytesSent = 0;
    // отправляем данные
    iResult = WSASend(hSocket, &wsaSendingBuffer, 1, &dwBytesSent, 0, &wsaOverlapped, NULL);
    iWSAError_1 = WSAGetLastError();
    bResult = GetQueuedCompletionStatus(hCompletionPort, &dwBytesCompleted, &ulCompletionKey, &pOverlap, INFINITE);
    iWSAError_2 = WSAGetLastError();

iResult = -1
dwBytesSent = 0
iWSAError_1 = 997
bResult = 1
dwBytesCompleted = 50000000
ulCompletionKey = 11
pOverlap = 12fc30
iWSAError_2 = 997

Возвращаюсь из GetQueuedCompletionStatus до того, как все данные доставлены принимающей стороне. Хотелось бы вернуться из GetQueuedCompletionStatus только после того, как все данные доставлены и подтверждены. Возможно ли это?

Полностью:
    // создаем сокет
    hSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
    if(hSocket != INVALID_SOCKET)
    {
        // инициализируем адрес
        RemoteAddress.sin_family = AF_INET;
        RemoteAddress.sin_addr.S_un.S_addr = inet_addr(pIP);
        RemoteAddress.sin_port = htons(1157);
        // соединяемся
        iResult = WSAConnect(hSocket, (SOCKADDR*) &RemoteAddress, sizeof(sockaddr_in), NULL, NULL, NULL, NULL);
        if(iResult == 0)
        {
            pOptionValue = (char*) malloc(sizeof(int));
            iOptionLength = sizeof(int);
            *(int*)pOptionValue = 0;
            iResult = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, pOptionValue, iOptionLength);

            hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 10);
            hCompletionPort = CreateIoCompletionPort((HANDLE)hSocket, hCompletionPort, 11, 10);

            wsaOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, "");
            wsaOverlapped.Internal = 0;
            wsaOverlapped.InternalHigh = 0;
            wsaOverlapped.Offset = 0;
            wsaOverlapped.OffsetHigh = 0;
            wsaOverlapped.Pointer = NULL;

            wsaSendingBuffer.buf = (char*) malloc(50000000);
            wsaSendingBuffer.len = 50000000;

            dwBytesSent = 0;
            // отправляем данные
            iResult = WSASend(hSocket, &wsaSendingBuffer, 1, &dwBytesSent, 0, &wsaOverlapped, NULL);
            iWSAError_1 = WSAGetLastError();
            bResult = GetQueuedCompletionStatus(hCompletionPort, &dwBytesCompleted, &ulCompletionKey, &pOverlap, INFINITE);
            iWSAError_2 = WSAGetLastError();

            printf("\niResult = %d", iResult);
            printf("\ndwBytesSent = %d", dwBytesSent);
            printf("\niWSAError_1 = %d", iWSAError_1);
            printf("\nbResult = %d", bResult);
            printf("\ndwBytesCompleted = %d", dwBytesCompleted);
            printf("\nulCompletionKey = %d", ulCompletionKey);
            printf("\npOverlap = %x", pOverlap);
            printf("\niWSAError_2 = %d", iWSAError_2);
            printf("\n");


Если убрать строки установки опции SO_SNDBUF
    pOptionValue = (char*) malloc(sizeof(int));
    iOptionLength = sizeof(int);
    *(int*)pOptionValue = 0;
    iResult = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, pOptionValue, iOptionLength);


то результат будет такой

iResult = 0
dwBytesSent = 50000000
iWSAError_1 = 0
bResult = 1
dwBytesCompleted = 50000000
ulCompletionKey = 11
pOverlap = 12fc30
iWSAError_2 = 0

то есть операция WSASend() даже отложенной не является, а якобы уже выполнена.
Re: Есть вопрос по работе с сокетом через порты завершения в
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 18.10.11 10:23
Оценка:
Если есть время и желание разобраться в нюансах IOCP, то рекомендую посмотреть исходники Boost.Asio.
Например, completion packet может приходить в GetQueuedCompletionStatus раньше, чем произойдет выход из соотв. WSASend/WSARecv. Мало кто об этом знает. И в MSDN кроме предложения использовать "какой-нибудь reference counting" ничто не намекает на эту особенность.
Programs must be written for people to read, and only incidentally for machines to execute
Re[6]: Есть вопрос по работе с сокетом через порты завершени
От: acDev Россия  
Дата: 18.10.11 10:27
Оценка:
Здравствуйте, DenRaskatov, Вы писали:

DR>Возвращаюсь из GetQueuedCompletionStatus до того, как все данные доставлены принимающей стороне. Хотелось бы вернуться из GetQueuedCompletionStatus только после того, как все данные доставлены и подтверждены. Возможно ли это?

DR> wsaOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, "");

Так вы указываете Event. Вот поэтому то, видимо, у вас такой результат.
Да и вообще не понятно зачем вам IOCP, коли WSASend и GetQueuedCompletionStatus в одном потоке ...
Re[5]: Есть вопрос по работе с сокетом через порты завершени
От: DenRaskatov  
Дата: 18.10.11 10:29
Оценка:
В MSDN пишут:
A completion indication will occur, invoking the completion of a routine or setting of an event object, when the buffer(s) have been consumed by the transport.
Сие видимо означает, что отправка считается завершенной, когда данные скопированы в буфер протокола транспортного уровня .
Re[7]: Есть вопрос по работе с сокетом через порты завершени
От: DenRaskatov  
Дата: 18.10.11 10:33
Оценка:
Здравствуйте, acDev, Вы писали:

D>Так вы указываете Event. Вот поэтому то, видимо, у вас такой результат.

Не понял, что Вы хотели сказать . Как же иначе?

D>Да и вообще не понятно зачем вам IOCP, коли WSASend и GetQueuedCompletionStatus в одном потоке ...

Это только для простоты примера.
Re[6]: Есть вопрос по работе с сокетом через порты завершени
От: acDev Россия  
Дата: 18.10.11 11:07
Оценка:
Здравствуйте, DenRaskatov, Вы писали:

DR>В MSDN пишут:

DR>A completion indication will occur, invoking the completion of a routine or setting of an event object, when the buffer(s) have been consumed by the transport.
DR>Сие видимо означает, что отправка считается завершенной, когда данные скопированы в буфер протокола транспортного уровня .

Вот что писали по этому поводу: http://rsdn.ru/forum/network/283985.aspx
Автор: Gosha
Дата: 02.06.03


D>>Так вы указываете Event. Вот поэтому то, видимо, у вас такой результат.

DR>Не понял, что Вы хотели сказать . Как же иначе?

Просто у меня чистый IOCP с пулом потоков.
А ты видимо хочешь событийный сервак написать или просто не понимаешь для чего в структуре WSAOVERLAPPED есть hEvent.
Re[7]: Есть вопрос по работе с сокетом через порты завершени
От: DenRaskatov  
Дата: 18.10.11 11:19
Оценка:
Здравствуйте, acDev, Вы писали:

D>Вот что писали по этому поводу: http://rsdn.ru/forum/network/283985.aspx
Автор: Gosha
Дата: 02.06.03

Теперь стало понятней, спасибо за Ваши ответы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.