IOCP/WSARecv Access Violation
От: Zurbarah  
Дата: 19.11.09 13:57
Оценка:
Натолкнулся на странную ошибку с IOCP, при выполнении некоторых вызовов WSARecv происходит Access Violation внутри системного кода, после этого socket не работает. Вот здесь http://www.rsdn.ru/forum/network/3441036.1.aspx
Автор: Armastab
Дата: 24.06.09
, нечто подобное уже обсуждалось, но решения так и не было.

Ниже минимальный код сервера на котором эта ошибка воспроизводится, один поток, сервер накапливает "пакеты" по 3 байта. С качестве клиента, можно использовать обычный telnet на порт 3030. Ошибка возникает когда переданный с клиента кусок больше чем тот который сервер ожидает в данный момент. Шаги для воспроизведения: заходим через telnet mycomp 3030, сервер просит 3 байта, нажимает любую букву, 1 байт передаётся на сервер, сервер получает 1 байт и ждет 2 байта, нажимаем функциональную клавишу (F1), на сервер передаётся 3 байта, сервер получает 2 байта, следующий вызов WSARecv приводит к ошибке.

WDYT?

#include <winsock2.h>
#include <tchar.h>
#include <stdio.h>

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    SOCKET ListenSocket, AcceptSocket;
    struct sockaddr_in saClient;
    int iClientSize = sizeof(saClient);
    u_short port = 3030;
    char* ip;
    sockaddr_in service;
    int error;

    error = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (error)
        return -1;

    ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (ListenSocket == INVALID_SOCKET)
        return -1;

    service.sin_family = AF_INET;
    service.sin_port = htons(port);
    hostent* thisHost;
    thisHost = gethostbyname("");
    ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);
    service.sin_addr.s_addr = inet_addr(ip);

    error = bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));
    if (error == SOCKET_ERROR)
        return -1;

    error = listen(ListenSocket, 1);
    if (error == SOCKET_ERROR)
        return -1;

    AcceptSocket = WSAAccept(ListenSocket, (SOCKADDR*) &saClient, &iClientSize, NULL, NULL);

    HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    CreateIoCompletionPort((HANDLE)AcceptSocket, CompletionPort, 0, 0);

    size_t nDataSize = 3;
    size_t nDataComplete = 0;
    char pszData[256];
    while (true)
    {
        if (nDataComplete >= nDataSize)
        {
            printf("---\n");
            nDataComplete = 0;
        }

        WSABUF Buffer;
        Buffer.buf = pszData + nDataComplete;
        Buffer.len = nDataSize - nDataComplete;

        WSAOVERLAPPED Overlapped;
        ZeroMemory(&Overlapped, sizeof(Overlapped));

        DWORD nFlags = 0;
        if (!WSARecv(AcceptSocket, &Buffer, 1, NULL, &nFlags, &Overlapped, NULL))
            return -1;

        DWORD nError;
        DWORD nBytesTransferred;
        WSAOVERLAPPED* pOverlapped = NULL;
        while (true)
        {
            DWORD nKey;
            if (!::GetQueuedCompletionStatus(CompletionPort, &nBytesTransferred, &nKey, &pOverlapped, 0))
            {
                nError = ::GetLastError();
                if (nError == WAIT_TIMEOUT)
                    continue;

                return -1;
            }
            else
            {
                nError = ::GetLastError();
                if (nError == ERROR_IO_PENDING)
                    continue;

                break;
            }
        }

        nDataComplete += nBytesTransferred;

        printf("%i\n", nBytesTransferred);
    }

    closesocket(AcceptSocket);
    closesocket(ListenSocket);
    WSACleanup();

    return 0;
}
iocp
Re: IOCP/WSARecv Access Violation
От: TarasCo  
Дата: 19.11.09 21:17
Оценка:
У меня два вопроса:
1) может ли WSARecv вернуть ошибку, если запрос не может быть удовлетворен немедленно ( данных еще нет )?
2) можно как нибудь организовать ожидание завершения запроса WSARecv?
3) что будет, если вызвать 2 раза подряд WSARecv и в оба вызова сунуть один и тот же буфер и одну и ту же структуру WSAOVERLAPPED ?
4) можно ли в однопоточном приложении обойтись без IOCP ?
Да пребудет с тобою сила
Re[2]: IOCP/WSARecv Access Violation
От: Zurbarah  
Дата: 20.11.09 13:06
Оценка:
TC>1) может ли WSARecv вернуть ошибку, если запрос не может быть удовлетворен немедленно ( данных еще нет )?
TC>2) можно как нибудь организовать ожидание завершения запроса WSARecv?
Да и да. Базовые вопросы, хорошо описаны в документации, почитай лучше там.

TC>3) что будет, если вызвать 2 раза подряд WSARecv и в оба вызова сунуть один и тот же буфер и одну и ту же структуру WSAOVERLAPPED ?

Ничего хорошего не будет, конкретные неприятности зависят от разных факторов. Повторное использование одной структуры WSAOVERLAPPED допустимо только после её обработки через GetQueuedCompletionStatus.

TC>4) можно ли в однопоточном приложении обойтись без IOCP ?

Нужно. IOCP обычно выбирают не из-за удобства, а из-за скорости работы при больших нагрузках. Однопоточное приложение большую нагрузку не потянет в силу ограничений архитектуры и мудрить с IOCP смысла не вижу.
Re: IOCP/WSARecv Access Violation
От: Michael Chelnokov Украина  
Дата: 21.11.09 13:22
Оценка: +1
Здравствуйте, Zurbarah, Вы писали:

Z> if (!WSARecv(AcceptSocket, &Buffer, 1, NULL, &nFlags, &Overlapped, NULL))

Z> return -1;

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