Порты завершения ввода-вывода

Тема в разделе "WASM.NETWORKS", создана пользователем kool, 22 июн 2007.

  1. kool

    kool New Member

    Публикаций:
    0
    Регистрация:
    6 июн 2007
    Сообщения:
    15
    Помогите разобраться с программированием сокетов.
    (текст программ в прикрепленном файле)
    Функция GetQueuedCompletionStatus() срабатывет только когда клиент вызывает connect. И то
    GetQueuedCompletionStatus() выдает ошибку что поток такой-то завершен с кодом таким-то (обычно 0).
    При передаче данных клиентом ф-ция GetQueuedCompletionStatus() не срабатывает вообще, хотя если перед
    ней поставить recv, то все принимается как надо но только для recv, а GetQueuedCompletionStatus() по прежнему не работает.
    В тексте сервера переставлял вызов потока
    // Создание рабочего потока сервера и передача.
    // порта завершения в качестве параметра.
    ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort, 0, &ThreadID);
    // Закрытие описателя потока.
    CloseHandle(ThreadHandle);
    и после Шага 6. Но GetQueuedCompletionStatus() по прежнему не реагирует.
    Помогите разобраться с этими портами завершения и сокетами.
    В теории вроде все гладко, а на практике не работает.
    Спасибо всем за полезные ответы.
     
  2. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Нужно примерно так

    Код (Text):
    1. err      = GetQueuedCompletionStatus(...);
    2. wsaerr = WSAGetLastError();
    3.  
    4. if (!err) {
    5.  
    6.    if (...)
    7.        Бла-бла;
    8. }
    Кстати, вот кусок обработки от Джефри:

    Код (Text):
    1. DWORD dwNumBytes;
    2. ULONG_PTR CompKey;
    3. OVERLAPPED* pOverlapped;
    4.  
    5. // hIOCP is initialized somewhere else in the program
    6. BOOL fOk = GetQueuedCompletionStatus(hIOCP,
    7.    &dwNumBytes, &CompKey, &pOverlapped, 1000);
    8. DWORD dwError = GetLastError();
    9.  
    10. if (fOk) {
    11.    // Process a successfully completed I/O request
    12. } else {
    13.    if (pOverlapped != NULL) {
    14.       // Process a failed completed I/O request
    15.       // dwError contains the reason for failure
    16.    } else {
    17.       if (dwError == WAIT_TIMEOUT) {
    18.          // Time-out while waiting for completed I/O entry
    19.       } else {
    20.          // Bad call to GetQueuedCompletionStatus
    21.          // dwError contains the reason for the bad call
    22.       }
    23.    }
    24. }
    а вот вырезка из мсдн о возвращаемом значении:

    If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped parameters.

    If *lpOverlapped is NULL and the function does not dequeue a completion packet from the completion port, the return value is zero. The function does not store information in the variables pointed to by the lpNumberOfBytes and lpCompletionKey parameters. To get extended error information, call GetLastError. If the function did not dequeue a completion packet because the wait timed out, GetLastError returns WAIT_TIMEOUT.

    If *lpOverlapped is not NULL and the function dequeues a completion packet for a failed I/O operation from the completion port, the return value is zero. The function stores information in the variables pointed to by lpNumberOfBytes, lpCompletionKey, and lpOverlapped. To get extended error information, call GetLastError.

    If a socket handle associated with a completion port is closed, GetQueuedCompletionStatus returns ERROR_SUCCESS, with *lpOverlapped non-NULL and lpNumberOfBytes equal zero.




    Короче, если ничего не помогает, натыкай printf'ов и смотри что к чему :)
     
  3. kool

    kool New Member

    Публикаций:
    0
    Регистрация:
    6 июн 2007
    Сообщения:
    15
    Наверное я неправильно выразился -
    GetQueuedCompletionStatus() - выдает не просто ошибку
    а вызывает исключение, так что блок
    try - except с ним не справляется.
    Конечно разные точки останова в виде printf и т.д. я уже ставил
    везде где только можно, но ошибка где-то в логике или параметрах.
     
  4. kool

    kool New Member

    Публикаций:
    0
    Регистрация:
    6 июн 2007
    Сообщения:
    15
    Если у кого есть текст с использованием портов в.в. клиента и сервера одновременно или хотя бы ссылка - поделитесь плиз.
     
  5. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Во-первых:
    Клиент подключается и отправляет серверу данные. Ты же в сервере в самом начале цикла ждёшь _завершения_ операции, а ты её выдавал? Сначала нужно сделать WSARecv, а уже потом GetQueuedCompletionStatus();

    Код (Text):
    1. PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
    2. printf("Socket number %d connected\n", Accept);
    3. PerHandleData->Socket = Accept;
    4.  
    5. // Шаг 7:
    6. // Привязка сокета к порту завершения
    7. CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData, 0);
    Вот после седьмого шага и выдай запрос на чтение данных из сокета.

    Во-вторых:
    При выдаче OVERLAPPED-запроса (пусть будет на чтение) ты передаешь WSARecv _указатель_ на структуру OVERLAPPED, этот же _указатель_ тебе должен прийти в параметре (&PerIoData), но тут у тебя ошибка - это должен быть
    не адрес структуры, а _указатель_ на неё, то есть ты через этот указатель должен снова получить доступ к структуре OVERLAPPED, которую выдавал в WSARecv();
    Соответственно нужно изменить код:

    Код (Text):
    1. PER_IO_OPERATION_DATA *PerIoData;
    2.  
    3. err = GetQueuedCompletionStatus(...(LPOVERLAPPED *)(&PerIoData), INFINITE);
    Чтобы меньше путаться c виндовыми поинтерами, лучше пиши просто (OVERLAPPED**), вместо (LPOVERLAPPED*).
     
  6. kool

    kool New Member

    Публикаций:
    0
    Регистрация:
    6 июн 2007
    Сообщения:
    15
    Огромное спасибо за ответ. Самому надо было догадаться
    что что-то не так с параметрами ф-ции при такой ошибке.
    Да вот я как раз после седьмого шага и
    выдавал запрос на получение данных (в прикрепленном файле я это не записал
    но там есть намек под комментарием).Тоже самое написано в книге откуда этот пример.
    Но у меня это не работает, может быть потому что рабочий поток запускается
    раньше чем выполняется WSARecv (в этот момент ведь даже сокет не создан). Теперь (после исправлений
    LPPER_IO_OPERATION_DATA PerIoData) ф-ция GetQueuedCompletionStatus()
    срабатывает постоянно (без ошибок-исключений),
    поскольку, действительно, никакой операции перед этим не производилось.
    Где же всетаки нужно ставить запрос WSARecv? Может до создания рабочего потока?
    nester7 объясни плиз почему нужно ставить именно после седьмого шага.
     
  7. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Сам по себе седьмой шаг тут ни при чём. Смысл в том, что бы об асинхронных операция ввода-вывода на дескрипторе (в данном случае на сокете) получать уведомления через порт завершения, отсюда вывод - запросы нужно выдавать только _после_ того, как дескриптор "привязали" к порту завершения. Соответственно ты можешь после привязки выдавать хоть тонну запросов к сокету; как только они завершатся - они будут стоять в очереди порта завершения, до тех пор, пока ты их оттуда не вычитаешь функцией GetQueueudCompletionStatus(); твой сервернй поток может ещё и не успеть создаться, но запросы, если они уже завершились, будут тебя ждать в порту.

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

    А вот и ещё: вдумчиво читать вот это
    http://rsdn.ru/res/book/win32/programming_server_apps_w2k.xml
    Глава 2. Ввод-вывод и межпотоковое взаимодействие
    :)
     
  8. kool

    kool New Member

    Публикаций:
    0
    Регистрация:
    6 июн 2007
    Сообщения:
    15
    В том то и дело что поток успевает создаваться и выполняется
    его функция (я ставил MessageBox-ы), в которой и вызывается GetQueuedCompletionStatus(), а соединение еще не установлено (если поток создавать до вызова Accept как у меня в примере).
    При этом GetQueuedCompletionStatus() возвращает нуль и продолжает крутиться в цикле. Исходники я не рипал, а восстанавливал из случайно попавшегося под руку руководства, лучшего к сожалению не нашел (кстати там и ошибка с этим указателем была, но в другой форме). Книжка конечно хорошая, но не достать - уже раньше пытался.
     
  9. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    http://dstu2204.narod.ru/6sem/spo/