IOCP C ++ TCP Client

I am having trouble implementing the TCP IOCP client. I implemented kqueue on Mac OSX, so I wanted to do something similar on windows, and I understand that IOCP is the closest. The main problem is that GetCompetetionStatus never returns and always shuts down. I assume that I have something missing when creating a descriptor for control, but I donโ€™t know what. I still got this:

My connection procedure: (for clarity, remove some error)

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; 

Here is the sending procedure: (also for clarity).

 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; 

}

Any insight would be appreciated.

+6
source share
2 answers

You are connecting the same socket to multiple IOCompletionPorts. I am sure this is not true. In your IOPortConnect function (where you are recording) you call CreateIOCompletionPort 4 times, passing in the same handle.

My advice:

  • Create one IOCompletion port (which ultimately binds multiple sockets).
  • Create a pool of worker threads (calling CreateThread), each of which then blocks the IOCompletionPort handle, calling GetQueuedCompletionStatus in a loop.
  • Create one or more WSA_OVERLAPPED sockets and associate them with IOCompletionPort.
  • Use the WSA socket functions that accept OVERLAPPED * to trigger overlapping operations.
  • Handle the completion of issued queries when workflows are returned from GetQueuedCompletionStatus using the OVERLAPPED * that you passed in to start the operation.

Note. WSASend returns both 0 and SOCKET_ERROR with WSAGetLastError () as WSA_IO_PENDING as codes to indicate that you will receive an I / O completion packet received in GetQueuedCompletionStatus. Any other error code means that you should immediately handle the error because the I / O operation was not queued, so there will be no further callbacks.

Note2: OVERLAPPED * passed to the WSASend function (or any other) is OVERLAPPED * returned from GetQueuedCompletionStatus. You can use this fact to convey additional context information with a call:

 struct MYOVERLAPPED { OVERLAPPED ovl; }; MYOVERLAPPED ctx; WSASend(...,&ctx.ovl); ... OVERLAPPED* pov; if(GetQueuedCompletionStatus(...,&pov,...)){ MYOVERLAPPED* pCtx = (MYOVERLAPPED*)pov; 
+5
source

Chris dealt with most of the problems, and you probably already looked at a lot of code examples, but ...

I have a free IOCP code that is available here: http://www.serverframework.com/products---the-free-framework.html

There are also some of my CodeProject articles on a topic related to this page.

+2
source

Source: https://habr.com/ru/post/886246/


All Articles