views:

386

answers:

1

Dear everybody,
I have a problem using asio. My client/server application requires only synchronous communication. So, using the examples for synchro from the boost homepage, I have set up two procedures to send and receive data. Their code is as follows:

void vReceive(tcp::socket & socket, std::string & szDest){
    char szTmp_Buf [BUF_LEN + 1];
    szDest = "";
    std::cout << "Entering vReceive . . ." << std::endl;

    for (;;){
      char szBuf [BUF_LEN];
      boost::system::error_code error;
      uInt uiBytes_Recv = socket.read_some(boost::asio::buffer(szBuf), error);
      std::cout << " Read " << uiBytes_Recv << " bytes" << std::endl;
      if (error == boost::asio::error::eof)
         break; // Connection closed cleanly by peer.
      else if (error)
         throw boost::system::system_error(error); // Some other error.

      memcpy((void*) szTmp_Buf, (void*) szBuf, uiBytes_Recv );
      szTmp_Buf[ uiBytes_Recv ] = '\0';
      szDest += szTmp_Buf;
      };
      std::cout << "Received" << szDest << std::endl;
      std::cout << "Leaving vReceive . . ." << std::endl << std::endl;
   };

void vSend(tcp::socket & socket, std::string & szSrc){
    std::cout << "Entering vSend . . . " << std::endl;
    std::cout << "Sending " << szSrc << std::endl;
    boost::system::error_code ignored_error;
    boost::asio::write(socket, boost::asio::buffer(szSrc), boost::asio::transfer_all(), ignored_error);
    std::cout << "Leaving vSend . . . " << std::endl << std::endl;
    };

These procedures are just wrappers for the lines of code extracted from the boost examples.

In my test applications, the client calls

std::string szDate;
vReceive(socket, szDate);
vSend(socket, std::string("Chop Suey!") );
vReceive(socket, szDate);
vSend(socket, std::string("Halo"));

and the server calls

std::string message = make_daytime_string();
std::string szReceived;
vSend(socket, message);
vReceive(socket, szReceived);
vSend(socket, message);
vReceive(socket, szReceived);

just to test the functionality. The problem is that both applications freeze after the first information exchange, as I have depicted on the following picture. It seems that vReceive() procedure on the client side doesn´t finish while vSend() finishes on the server side. So, does anybody have any idea, what might be wrong?

Just in case someone wanted to replicate the problem, I uploaded the sources complete sources to to same server, where the picture is in the asio_problem.rar file (I can have one hyperlink per post as a new member).

Thank you all in advance, Daniel.

+1  A: 

I think your problem lies here:

for (;;){
  /* snipped */

  if (error == boost::asio::error::eof)
     break; // Connection closed cleanly by peer.
  else if (error)
     throw boost::system::system_error(error); // Some other error.

  /* snipped */
}

You are looping infinitely until the connection closes (the only way to break out of the loop is if you receive an EOF error from the socket), but vSend never closes the socket. This means that vReceive in your client will be waiting forever, always expecting more data. Putting a socket.close() in vSend should do the trick nicely.

Of course, this means that your client and server will need to re-open the socket after each respective call to vReceive and vSend. If that's not desirable, then I recommend using some other means of the sender notifying the receiver that no more data will be sent.

I'm not sure if you have any constraints on the data being sent over the wire, but either sending a "data length" value at the beginning of your message or some sort of "end-of-data" sentinel value at the end would give you a nice, clean indicator of when to stop listening for more data.

This approach has the additional upside of giving you reason to use ASIO's read_until (or similar) functionality to handle the logic of waiting for the end of the data for you.

Brian