2010-12-03 4 views
2

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

Что I've пытался до сих пор проходил пользователем структуру, (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-strucutre?

EDIT: Как я могу успешно передать «данные для каждого соединения»? ... Похоже, что я получил искусство делать это (как описано в двух ссылках выше) неправильно.

Вот идет мой код: (Да, его уродливую и его единственный тест-код)

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); 
} 

ответ

1

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

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

4

Если вы передаете свой-структуру, как это должно работать нормально:

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


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

Редактировать для добавления на данные IO:

Одной из часто злоупотребляемых особенностей асинхронной apis является они не копируют структуру 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; 

На этой стороне особенно важно, что перекрывающийся структура является первым членом хелперов структуры, так как это делает его легко отбрасывать назад от ПЕРЕПОЛНЕННЫЙ * Апи дает нам и помощнику *, который мы действительно хотим.

+0

Вау ... Я уже пробовал это, и это не сработало O_o ... Я думаю, что я испортил разыменование ... – Incubbus 2010-12-03 10:51:11

+0

трагически легко получить этот вид неправильной ссылки. Компилятор, заставляющий нас бросать все время, не помогает. – 2010-12-03 12:21:18

0

я использую стандартные процедуры сокетов (розетка, closesocket, связывать, принимать, подключить ...) для создания/уничтожения и ReadFile/WriteFile для ввода/вывода, поскольку они позволяют использовать OVERLAPPED структуры.

После того, как ваш сокет принят или подключен, вы должны связать его с контекстом сеанса, который он обслуживает. Затем вы связываете свой сокет с IOCP и (в третьем параметре) предоставляете ему ссылку на контекст сеанса. МОКП не знает, что такое эта ссылка, и она не заботится ни об этом. Ссылка используется для ВАШЕГО использования, так что, когда вы получаете IOC через GetQueuedCompletionStatus, переменная, указанная параметром 3, будет заполнена ссылкой, чтобы вы сразу нашли контекст, связанный с событием сокета, и можете начать обслуживание события. Обычно я использую индексированную структуру, содержащую (среди прочего) объявление сокета, перекрываемую структуру, а также другие данные, относящиеся к сеансу. Ссылка, которую я передаю в CreateIoCompletionPort в параметре 3, будет индексом для элемента структуры, содержащего сокет.

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

Необходимо также проверить перекрываемую структуру, чтобы убедиться в правильности ввода-вывода.

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

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