AcceptEx возвращает 1022 (WSAEINVAL) Что я сделал не так?

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

http://codepad.org/NEXG3Ssh ‹- код на кодовой панели

и

StartWinsock();
 cout << "Winsock initiated\n";
 //Get the number of processors
 DWORD ulProcessors = GetNumberOfProcessors();
 cout << "Number of Processors/Threads, that will be used: " << ulProcessors << endl;
 //Create an completion port
 hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, ulProcessors);
 if(hCompletionPort == NULL)
  ErrorAbort("Could not create completion port");
 cout << "Completion Port created\n";

 //Create threads
 CreateThreads(ulProcessors);
 cout << "Threads created\n";

 //Create socket
 AcceptorSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 addrinfo *final, hints;
 memset(&hints, 0, sizeof(hints));
 hints.ai_family = AF_INET;
 hints.ai_flags = AI_PASSIVE;
 if(getaddrinfo(NULL,"12345", &hints, &final))
  ErrorAbort("Could not retrieve address information");
 if(bind(AcceptorSock,final->ai_addr, final->ai_addrlen))
  ErrorAbort("Could not bind socket");
 freeaddrinfo(final);
 cout << "Acceptor socket created and bound\nStarting to listen on the acceptor socket\n";
 if(listen(AcceptorSock, 2))
  ErrorAbort("Can´t listen on the socket");

 //Add acceptor socket file handle to be observed by the completion port
 if(CreateIoCompletionPort((HANDLE)AcceptorSock, hCompletionPort, NEW_CONNECTION, 0) != hCompletionPort)
  ErrorAbort("A new completion port has been created instead of using the existing one");
 cout << "Acceptor socket associated with the completion port\n";

 ResumeThreads(2);
 char lpOutputBuf[1024];
 int outBufLen = 1024;
 DWORD dwBytes;
 OVERLAPPED over;
 SOCKET newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


 while(true)
 {
  memset(&over, 0, sizeof(over));
  if(AcceptEx(AcceptorSock, newSock, lpOutputBuf, outBufLen - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in)+16, sizeof(sockaddr_in)+16, &dwBytes, &over) == FALSE)
  {
   int x = WSAGetLastError();
   if( x != WSA_IO_PENDING)
    ErrorAbort("Could not acceptex a new connection");
  }
 }

person Juarrow    schedule 03.12.2010    source источник
comment
Вам действительно не нужен этот цикл while (true) ... И если вы хотите опубликовать больше одного асинхронного принятия, вам нужно создать «newSock» для каждого вызова AcceptEx.   -  person Len Holgate    schedule 04.12.2010


Ответы (2)


Проблема в вашем коде заключается в следующем:

while(true)
{
  memset(&over, 0, sizeof(over));
  if(AcceptEx(AcceptorSock, newSock, lpOutputBuf, outBufLen - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in)+16, sizeof(sockaddr_in)+16, &dwBytes, &over) == FALSE)
  {
    int x = WSAGetLastError();
    if( x != WSA_IO_PENDING)
    ErrorAbort("Could not acceptex a new connection");
  }
}

Второй параметр функции AcceptEx (в данном случае newSock) должен быть неподключенным и несвязанным сокетом, тогда при поступлении нового подключения параметр newSock будет недопустимым параметром (поскольку теперь подключен), чтобы избежать этого, новый дескриптор сокета должен быть создан, но цикл должен ждать, пока не появится новое соединение, для этого необходимо использовать WSAEVENT. Сначала необходимо использовать функцию WSAEventSelect, чтобы связать сетевое событие FD_ACCEPT с WSAEVENT, это нужно сделать до создания AcceptThread:

g_ev = WSACreateEvent();
if (WSA_INVALID_EVENT == g_ev)
  ErrorAbort("Error occurred while WSACreateEvent()";
if (SOCKET_ERROR == WSAEventSelect(AcceptorSock, g_ev, FD_ACCEPT))
{
  WSACloseEvent(g_ev);
  ErrorAbort("Error occurred while WSAEventSelect().");
}

AcceptThread затем вызывает функцию AcceptEx и ждет нового соединения, когда приходит новое соединение, newSock добавляется в порт завершения и создается новый сокет, этот поток имеет только элементы, необходимые для приема соединений:

DWORD WINAPI AcceptThread(LPVOID lParam)
{
  SOCKET AcceptorSock = (SOCKET)lParam, new;
  SOCKET newSock; 
  OVERLAPPED over;    
  DWORD wr;

  memset(&over, 0, sizeof(over));
  do
  {
    newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(AcceptEx(AcceptorSock, newSock, lpOutputBuf, outBufLen - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in)+16, sizeof(sockaddr_in)+16, &dwBytes, &over) == FALSE)
    {
      int x = WSAGetLastError();
      if (x != ERROR_IO_PENDING)
      {
        cout << "Could not acceptex a new connection" << endl;
        return 1;
      }
      else
      {
        if (WSA_WAIT_TIMEOUT != (wr = WSAWaitForMultipleEvents(1,  &g_ev, FALSE, INFINITE, FALSE)))
        {
          WSAEnumNetworkEvents(AcceptorSock, g_ev, &WSAEvents);
          if ((WSAEvents.lNetworkEvents & FD_ACCEPT) &&  (0 == WSAEvents.iErrorCode[FD_ACCEPT_BIT]))
          {
            if (CreateIoCompletionPort(newSock, hCompletionPort, 0, 0) == NULL)
            {
              cout << "Error CreateIoCompletionPort" << endl;
              return 2;
            }
            WSAResetEvent(g_ev);
          }
        }
      }
    }
  }while(wr != WSA_WAIT_EVENT_0);
}

Если кому-то нужна дополнительная информация, эти ссылки могут быть полезны: http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedscalableapp6b.html http://www.codeproject.com/KB/IP/SimpleIOCPApp.aspx http://msdn.microsoft.com/en-us/magazine/cc302334.aspx

person Luis G. Costantini R.    schedule 03.12.2010
comment
Вы знаете, что такое порты завершения? - person Juarrow; 03.12.2010
comment
Да, но я не использовал AcceptEx, и я предположил, что эта функция использует WSAEventSelect для проверки входящих соединений, потому что WSAWaitForMultipleEvents должен вызываться в потоке принятия соединения, возможно, эти документы помогут вам: msdn.microsoft.com/en-us/magazine/cc302334.aspx codeproject.com/KB/IP/SimpleIOCPApp.aspx - person Luis G. Costantini R.; 03.12.2010
comment
Я извиняюсь за то, что был таким грубым. Я думал, что при установлении нового соединения один пакет завершения события будет поставлен в очередь на порт завершения... но, похоже, этого не происходит. Я хочу, чтобы рабочие потоки обрабатывали новый сокет подключения, а не родительскую функцию - person Juarrow; 03.12.2010
comment
Да, acceptex будет поставлен в очередь на порт завершения, когда произойдет принятие; при условии, что вы правильно его настроили. - person Len Holgate; 03.12.2010
comment
Итак, я сделал это неправильно... но я не хочу ждать, пока AcceptEx будет завершен в потоке, вызывающем AcceptEx... это то, что вы сделали в своем исправлении. - person Juarrow; 03.12.2010
comment
@Incubbus Я отредактировал свой ответ и добавил ветку принятия. Возможно, вы решили свою проблему, но я надеюсь, что эта информация будет полезна для других людей с такой же проблемой. - person Luis G. Costantini R.; 04.12.2010
comment
Просто код примера НЕ показывает, как вы должны использовать AcceptEx с IOCP. Нет необходимости использовать событие вообще, нет необходимости в каких-либо потоках, кроме обычных потоков IOCP (одно из преимуществ использования AcceptEx по сравнению с традиционным циклом accept()). Вы должны связать свой новый сокет с IOCP, прежде чем вызывать AcceptEx, И очень важно, чтобы вы вызывали setsockopt() с SO_UPDATE_ACCEPT_CONTEXT в принятом сокете ПОСЛЕ завершения AcceptEx... - person Len Holgate; 04.12.2010

SOCKET newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

следует использовать WSASocket, а не socket, и вы должны передать флаг WSA_FLAG_OVERLAPPED при создании сокета.

person Len Holgate    schedule 03.12.2010
comment
сокеты, созданные с помощью socket(), могут использоваться как перекрытые по умолчанию, потому что они перекрываются по умолчанию (это то, что говорит msdn) - person Juarrow; 03.12.2010