Как передать пользовательские данные в рабочий поток с помощью IOCP?

StackOverflow https://stackoverflow.com/questions/4344162

Вопрос

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

До сих пор я пробовал передавать пользовательскую структуру как (ULONG_PTR)&structure as ключ завершения в вызове ассоциации CreateIoCompletionPort()Но это не сработало.

Теперь я попытался определить свою собственную структуру OVERLAPPED и использовать CONTAINING_RECORD(), как описано здесь. http://msdn.microsoft.com/en-us/magazine/cc302334.aspx и http://msdn.microsoft.com/en-us/magazine/bb985148.aspx.Но это тоже не работает.(Я получаю странные значения содержимого pHelper)

Итак, мой вопрос:Как передать данные в рабочий поток с помощью WSARecv(), GetQueuedCompletionStatus() и пакета завершения или структуры OVERLAPPED?

РЕДАКТИРОВАТЬ:Как я могу успешно передавать «данные для каждого соединения»?...Кажется, я неправильно овладел искусством этого (как описано в двух ссылках выше).

Вот мой код:(Да, это уродливо и это единственный ТЕСТ-код)

struct helper
    {
        SOCKET m_sock;
        unsigned int m_key;
        OVERLAPPED over;
    };


///////

SOCKET newSock = INVALID_SOCKET;
    WSABUF wsabuffer;
    char cbuf[250];
    wsabuffer.buf = cbuf;
    wsabuffer.len = 250;
    DWORD flags, bytesrecvd;


    while(true)
    {
        newSock = accept(AcceptorSock, NULL, NULL);
        if(newSock == INVALID_SOCKET)
            ErrorAbort("could not accept a connection");

        //associate socket with the CP
        if(CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, 3,0) != hCompletionPort)
            ErrorAbort("Wrong port associated with the connection");
        else
            cout << "New Connection made and associated\n";

        helper* pHelper = new helper;
        pHelper->m_key = 3;
        pHelper->m_sock = newSock;
        memset(&(pHelper->over), 0, sizeof(OVERLAPPED));
        flags = 0;
        bytesrecvd = 0;

        if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, (OVERLAPPED*)pHelper, NULL) != 0)
        {
            if(WSAGetLastError() != WSA_IO_PENDING)
                ErrorAbort("WSARecv didnt work");
        }
    }

    //Cleanup
    CloseHandle(hCompletionPort);
    cin.get();
    return 0;
}

DWORD WINAPI ThreadProc(HANDLE h)
{
    DWORD dwNumberOfBytes = 0;
    OVERLAPPED* pOver = nullptr;
    helper* pHelper = nullptr;
    WSABUF RecvBuf;
    char cBuffer[250];
    RecvBuf.buf = cBuffer;
    RecvBuf.len = 250;
    DWORD dwRecvBytes = 0;
    DWORD dwFlags = 0;
    ULONG_PTR Key = 0;

    GetQueuedCompletionStatus(h, &dwNumberOfBytes, &Key, &pOver, INFINITE);

    //Extract helper
    pHelper = (helper*)CONTAINING_RECORD(pOver, helper, over);


    cout << "Received Overlapped item" << endl;
    if(WSARecv(pHelper->m_sock, &RecvBuf, 1, &dwRecvBytes, &dwFlags, pOver, NULL) != 0)
        cout << "Could not receive data\n";
    else
        cout << "Data Received: " << RecvBuf.buf << endl;

    ExitThread(0);
}
Это было полезно?

Решение

Вы можете отправить самые данные специального назначения в порт завершения через СостоявливатьсяCompletionStatus..

Пакет завершения ввода-вывода удовлетворит выдающийся вызов функции GetQueuedCompletionStatus. Эта функция возвращается с тремя значениями, передаваемыми в качестве второго, третьего и четвертого параметра вызова на поток. Система не использует или проверяет эти значения. В частности, параметр lpoverbaped не должен указывать на перекрытую структуру.

Другие советы

Если вы передадите свою структуру следующим образом, она должна работать нормально:

helper* pHelper = new helper;
CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, (ULONG_PTR)pHelper,0);
...


helper* pHelper=NULL;
GetQueuedCompletionStatus(h, &dwNumberOfBytes, (PULONG_PTR)&pHelper, &pOver, INFINITE);

Изменить, чтобы добавить данные для каждого ввода-вывода:

Одна из особенностей асинхронных API, которыми часто злоупотребляют, заключается в том, что они не копируют структуру OVERLAPPED, а просто используют предоставленную — следовательно, перекрывающаяся структура, возвращаемая из GetQueuedCompletionStatus, указывает на исходно предоставленную структуру.Так:

struct helper {
  OVERLAPPED m_over;
  SOCKET     m_socket;
  UINT       m_key;
};

if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, &pHelper->m_over, NULL) != 0)

Обратите внимание, что в исходном примере вы снова неправильно провели кастинг.(OVERLAPPED*)pHelper передавал указатель на START вспомогательной структуры, но часть OVERLAPPED была объявлена ​​последней.Я изменил его, чтобы передать адрес фактической перекрывающейся части, а это означает, что код компилируется без приведения, что позволяет нам знать мы делаем правильные вещи.Я также переместил перекрывающуюся структуру, чтобы она стала первым членом структуры.

Чтобы перехватить данные на другой стороне:

OVERLAPPED* pOver;
ULONG_PTR key;
if(GetQueuedCompletionStatus(h,&dw,&key,&pOver,INFINITE))
{
  // c cast
  helper* pConnData = (helper*)pOver;

С этой стороны особенно важно, чтобы перекрывающаяся структура была первым членом вспомогательной структуры, поскольку это позволяет легко выполнить обратное преобразование из OVERLAPPED*, который нам дает API, и помощника*, который нам действительно нужен.

Я использую стандартные процедуры сокета (розетка, Clockesocket, BING, ACCECT, CONNECTS ...) для создания / уничтожения / съемки / записи / записи для ввода / вывода, поскольку они позволяют использовать перекрытую структуру.

После того, как ваш сокет принял или подключил, вам следует связать его с контекстом сеанса, который ИТ-услуги. Затем вы связываете свой сокет к IOCP и (в третьем параметре) предоставляете его ссылку на контекст сеанса. IOCP не знает, что такое эта ссылка и не заботится ни за этот вопрос. Ссылка предназначена для вашего использования, чтобы, когда вы получаете IOC через GetQueuedCompletionStatus, переменная, указанная на параметре 3, будет заполнена со ссылкой, так что вы немедленно найдите контекст, связанный с событием сокета и можете начать обслуживание события. Я обычно использую индексируемую структуру, содержащую (среди прочего) декларацию разъема, перекрывающуюся структуру, а также другие конкретные сеансы данных. Ссылка, которую я передаю в CreateoColletionPort в параметре 3, будет индексом для элемента структуры, содержащего розетку.

Вам нужно проверить, вернул ли GetQueuedCompletionStatus по завершению или время ожидания. С тайм-аутом вы можете пройти через свою проиндексированную структуру и посмотреть (например), если один из них получил время или что-то еще и принять подходящие действия в домашних условиях.

Перекрывающаяся структура также должна быть проверена, чтобы увидеть, что ввод / вывод выполнен правильно.

Функция, обслуживающая ICP, должна быть отдельной многопоточной сущностью. Используйте то же количество потоков, которые у вас есть ядра в вашей системе, или, по крайней мере, не более того, как оно тратит системные ресурсы (у вас нет больше ресурсов для обслуживания события, чем количество сердечников в вашей системе, верно?) Отказ

IOCPS действительно лучший из всех миров (слишком хорошо, чтобы быть правдой), и любой, кто говорит «один нить на сокет» или «ждать в списке нескольких сокетов в одной функции», не знаю, о чем они говорят. Первые подчеркивает ваш планировщик и последний опрос и опрос всегда чрезвычайно расточительны.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top