TCP-клиент IOCP C ++ - PullRequest
       27

TCP-клиент IOCP C ++

6 голосов
/ 19 апреля 2011

У меня возникли проблемы с реализацией клиента TCP IOCP. Я реализовал kqueue на Mac OSX, поэтому хотел сделать что-то похожее на Windows, и я понимаю, что IOCP - самая близкая вещь. Основная проблема заключается в том, что GetCompetetionStatus никогда не возвращается и всегда истекает. Я предполагаю, что я что-то упускаю при создании дескриптора для мониторинга, но не уверен что. Вот где я дошел:

Моя процедура подключения: (для ясности удалите обработку ошибок)

struct sockaddr_in server;
struct hostent *hp;
SOCKET sckfd;
WSADATA wsaData;

int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );


if ((hp = gethostbyname(host)) == NULL)
    return NULL;
WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED)
if ((sckfd = WSASocket(AF_INET,SOCK_STREAM,0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
    printf("Error at socket(): Socket\n");
    WSACleanup();
    return NULL;
}

server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr = *((struct in_addr *)hp->h_addr);
memset(&(server.sin_zero), 0, 8);

//non zero means non blocking. 0 is blocking.
u_long iMode = -1;
iResult = ioctlsocket(sckfd, FIONBIO, &iMode);
if (iResult != NO_ERROR)
    printf("ioctlsocket failed with error: %ld\n", iResult);


HANDLE hNewIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0);
CreateIoCompletionPort((HANDLE)sckfd, hNewIOCP , ulKey, 0);

connect(sckfd, (struct sockaddr *)&server, sizeof(struct sockaddr));

//WSAConnect(sckfd, (struct sockaddr *)&server, sizeof(struct sockaddr),NULL,NULL,NULL,NULL);

return sckfd;   

Вот процедура отправки: (для ясности также удалите некоторую обработку ошибок)

IOPortConnect(int ServerSocket,int timeout,string& data){

char buf[BUFSIZE];
strcpy(buf,data.c_str());
WSABUF buffer = { BUFSIZE,buf };
DWORD bytes_recvd;
int r;
ULONG_PTR ulKey = 0;
OVERLAPPED overlapped;
 OVERLAPPED* pov = NULL;
HANDLE port;

HANDLE hNewIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0);
CreateIoCompletionPort((HANDLE)ServerSocket, hNewIOCP , ulKey, 0);


BOOL get = GetQueuedCompletionStatus(hNewIOCP,&bytes_recvd,&ulKey,&pov,timeout*1000);

if(!get)
    printf("waiton server failed. Error: %d\n",WSAGetLastError());
if(!pov)
    printf("waiton server failed. Error: %d\n",WSAGetLastError());

port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long)0, 0);

SecureZeroMemory((PVOID) & overlapped, sizeof (WSAOVERLAPPED));

r = WSASend(ServerSocket, &buffer, 1, &bytes_recvd, NULL, &overlapped, NULL);
printf("WSA returned: %d WSALastError: %d\n",r,WSAGetLastError());
if(r != 0)
{
    printf("WSASend failed %d\n",GetLastError());
    printf("Bytes transfered: %d\n",bytes_recvd);
}
if (WSAGetLastError() == WSA_IO_PENDING)
    printf("we are async.\n");
CreateIoCompletionPort(port, &overlapped.hEvent,ulKey, 0);

BOOL test = GetQueuedCompletionStatus(port,&bytes_recvd,&ulKey,&pov,timeout*1000); 

CloseHandle(port);
return true;

}

Любое понимание будет оценено.

Ответы [ 2 ]

5 голосов
/ 19 апреля 2011

Вы связываете один и тот же сокет с несколькими портами IOCompletion. Я уверен, что это не верно. В вашей функции IOPortConnect (где вы выполняете запись) вы вызываете CreateIOCompletionPort 4 раза, передавая дескрипторы одного кадра.

Мой совет:

  • Создайте один порт IOCompletion (с которым, в конечном счете, вы будете ассоциировать множество сокетов).
  • Создайте пул рабочих потоков (вызывая CreateThread), каждый из которых затем блокирует дескриптор IOCompletionPort, вызывая GetQueuedCompletionStatus в цикле.
  • Создайте один или несколько сокетов WSA_OVERLAPPED и свяжите каждый из них с IOCompletionPort.
  • Используйте функции сокетов WSA, которые принимают OVERLAPPED * для запуска перекрывающихся операций.
  • Обрабатывает завершение выданных запросов, когда рабочие потоки возвращаются из GetQueuedCompletionStatus с OVERLAPPED *, который вы передали для начала операции.

Примечание: WSASend возвращает и 0, и SOCKET_ERROR с WSAGetLastError () как WSA_IO_PENDING в качестве кодов, указывающих, что вы получите пакет завершения ввода-вывода, поступающий в GetQueuedCompletionStatus. Любой другой код ошибки означает, что вы должны немедленно обработать ошибку, поскольку операция ввода-вывода не была поставлена ​​в очередь, поэтому дальнейших обратных вызовов не будет.

Примечание 2: OVERLAPPED *, передаваемый в функцию WSASend (или любую другую), является OVERLAPPED *, возвращаемым из GetQueuedCompletionStatus. Вы можете использовать этот факт для передачи дополнительной контекстной информации при вызове:

struct MYOVERLAPPED {
  OVERLAPPED ovl;
};
MYOVERLAPPED ctx;
WSASend(...,&ctx.ovl);
...
OVERLAPPED* pov;
if(GetQueuedCompletionStatus(...,&pov,...)){
  MYOVERLAPPED* pCtx = (MYOVERLAPPED*)pov;
2 голосов
/ 20 апреля 2011

Крис справился с большинством проблем, и вы, вероятно, уже рассмотрели множество примеров кода, но ...

У меня есть бесплатный код IOCP, который доступен здесь: http://www.serverframework.com/products---the-free-framework.html

Есть также несколько моих статей CodeProject по этой теме, связанных с этой страницей.

...