IO input port: how does WSARecv () work?

I want to write a server using a workflow pool and an I / O completion port. The server must process and forward messages between multiple clients. The data per client is in the ClientContext class. Data between instances of this class is exchanged using workflows. I think this is a typical scenario.

However, I have two problems with these I / O completion ports.

(1) The first problem is that the server mainly receives data from clients, but I never know if a complete message has been received. In fact, WSAGetLastError () always returns that WSARecv () is still waiting. I tried to wait for the OVERLAPPED.hEvent event with WaitForMultipleObjects (). However, it is blocked forever, i.e. WSARecv () never terminates in my program. My goal is to be absolutely sure that the entire message is received before further processing begins. My message has a "message length" field in its header, but I don’t see how to use it with the parameters of the IOCP function.

(2) If WSARecv () is commented out in the code snippet below, the program still receives data. What does it mean? Does this mean that I don’t need to call WSARecv () at all? I cannot get deterministic behavior with these I / O completion ports. Thanks for your help!

while(WaitForSingleObject(module_com->m_shutdown_event, 0)!= WAIT_OBJECT_0) { dequeue_result = GetQueuedCompletionStatus(module_com->m_h_io_completion_port, &transfered_bytes, (LPDWORD)&lp_completion_key, &p_ol, INFINITE); if (lp_completion_key == NULL) { //Shutting down break; } //Get client context current_context = (ClientContext *)lp_completion_key; //IOCP error if(dequeue_result == FALSE) { //... do some error handling... } else { // 'per client' data thread_state = current_context->GetState(); wsa_recv_buf = current_context->GetWSABUFPtr(); // 'per call' data this_overlapped = current_context->GetOVERLAPPEDPtr(); } while(thread_state != STATE_DONE) { switch(thread_state) { case STATE_INIT: //Check if completion packet has been posted by internal function or by WSARecv(), WSASend() if(transfered_bytes > 0) { dwFlags = 0; transf_now = 0; transf_result = WSARecv(current_context->GetSocket(), wsa_recv_buf, 1, &transf_now, &dwFlags, this_overlapped, NULL); if (SOCKET_ERROR == transf_result && WSAGetLastError() != WSA_IO_PENDING) { //...error handling... break; } // put received message into a message queue } else // (transfered_bytes == 0) { // Another context passed data to this context // and notified it via PostQueuedCompletionStatus(). } break; } } } 
+4
source share
1 answer

(1) The first problem is that the server mainly receives data from clients, but I never know if a message has been received.

Your recv calls can return from one byte to a whole "message". You need to enable the logic, which will work when it has enough data to work out the length of the full “message”, and then work when you really have the full “message”. As long as you DO NOT have enough data, you can reissue the recv call using the same memory buffer, but with an updated WSABUF structure that points to the end of the data that you have already received. This way you can accumulate the complete message in your buffer without having to copy data after each recv call completes.

(2) If WSARecv () is commented out in the code snippet below, the program is still receiving data. What do I mean? Does this mean that I do not need to call WSARecv ()?

I expect this to simply mean that you have a bug in your code ...

Note that it is “better” in terms of scalability not to use the event in an overlapping structure and instead bind the socket to IOCP and allow replenishment in the thread pool that deals with your terminations.

I have a free IOCP client / server infrastructure available from here that can give you some advice; and a series of articles on CodeProject (the first here: http://www.codeproject.com/KB/IP/jbsocketserver1.aspx ), where I deal with the whole problem of "reading full messages" (see "Splitting a byte stream").

+8
source

All Articles