Jishnu U Nair
Jishnu U Nair

Reputation: 520

TCP/IP client using Boost::asio

I'm trying to make a TCP/IP client using boost library. This is how I designed my program

->read thread to read from the server

->write thread to send commands

->a function that parses the read data from the server

int main()
{

TCP_IP_Connection router;
router.Create_Socket();
boost::thread_group t;
t.create_thread(boost::bind(&TCP_IP_Connection::get_status,&router,'i'));
t.create_thread(boost::bind(&TCP_IP_Connection::readTCP,&router));
std::string reply="\nend of main()";
std::cout<<reply;
t.join_all();
return 0;
}





void TCP_IP_Connection::Create_Socket()
{

tcp::resolver resolver(_io);//resolve into TCP endpoint
tcp::resolver::query query(routerip,rport);

tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 
//list of endpoints
tcp::resolver::iterator end;
boost::asio::streambuf b;
_socket = new tcp::socket(_io); //create socket
boost::system::error_code error= boost::asio::error::host_not_found;

try
 {
   while (error && endpoint_iterator != end) //if error go to next endpoint
  {
   _socket->close();
   _socket->connect(*endpoint_iterator++, error);
  }

if(error)
  throw boost::system::system_error(error);

 //else the router is connected
}

catch (std::exception& e)
 {
   std::cerr << e.what() << std::endl;
 }
}



void TCP_IP_Connection::get_status(char p)


{
    try
    {
     if(p=='i')
     _socket->send(boost::asio::buffer("llist\n\n"));
                  //sending command for input command
     else
        _socket->send(boost::asio::buffer(" sspo l1\n\n")); 
                  //sending signal presence for output command
    }
catch (std::exception& e)
 {
   std::cerr << e.what() << std::endl;
 }

}


void TCP_IP_Connection::readTCP()

 {

   this->len=0;
   boost::system::error_code error= boost::asio::error::host_not_found;
   try
   {    //loop reading all values from router
       while(1)
      {

       //wait for reply??

     _socket->async_read_some(boost::asio::buffer(this-
        >reply,sizeof(this>reply)),boost::bind(&TCP_IP_Connection::dataProcess,this,
  boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));

    _io.run();

    if(error==boost::asio::error::eof) //connection closed by router
       std::cout<<"connection closed by router";
       }

    }

  catch (std::exception& e)
   {
   std::cerr << e.what() << std::endl;
   }

}



 void TCP_IP_Connection::dataProcess(const boost::system::error_code &er,size_t l) 
{
if(!er)
{
if(l>0)
{
for(int i=0;i<l;i++)
{
this->data[i]=this->reply[i];
             //if(data[i]="\n")
 std::cout<<this->data[i];
 }
 }
 }
}

When I run the code all I get is the response from the server that says the client is connected and not the response of the command I send. But when I try debugging I get full output as I need. Am I doing anything wrong in the threading, or in the TCP read buffer.

Upvotes: 1

Views: 4170

Answers (1)

Bob Bryan
Bob Bryan

Reputation: 3847

Your code is creating 2 threads. The first thread created has a thread function called get_status. In get_status, there is no looping so it only executes the code once. It appears to be sending the string "llist\n\n" to the server and this is done synchronously. After that, it does not send anything else. So, are you expecting the server to send other data after the first command is sent? The code in the first thread may or may not execute completely before the code in the second thread executes.

The second thread is created and this thread appears to be responsible for processing information coming off of the socket. There is an infinite loop of while(1), but no logic to exit the loop so it will run forever unless an exception is thrown. I believe that the async_read_some method will not cause any data to be transferred until the buffer is full. The size of the buffer is specified by the size of reply. This may be your problem since the dataProcess method won't get called until all of the data specified by the length of reply has been received. In many protocols, the first 4 bytes specifies the length of the message. So, if you are dealing with variable length messages, then your code will have to take this into account.

One other item worth mentioning is that the looping code in readTCP to call _io.Run is not really necessary. You can add a work object to your io_service object in order for it to run continuously. For example:

void SSLSocket::InitAsynchIO()
{
   // This method is responsible for initiating asynch i/o.
   boost::system::error_code Err;
   string s;
   stringstream ss;
   //
   try
   {
      ss << "SSLSocket::InitAsynchIO: Worker thread - " << Logger::NumberToString(boost::this_thread::get_id()) << " started.\n";
      Log.LogString(ss.str(), LogInfo);
      // Enable the handlers for asynch i/o.  The thread will hang here until the stop method has been called or an error occurs.
      // Add a work object so the thread will be dedicated to handling asynch i/o.
      boost::asio::io_service::work work(*IOService);
      IOService->run();
      Log.LogString("SSLSocket::InitAsynchIO: receive worker thread done.\n", LogInfo);
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::InitAsynchIO: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}

It is ok to have your first thread do your first async read. Your read handler can be set up to call itself in order to handle the next message. For example:

void SSLSocket::HandleRead(const boost::system::error_code& error, size_t bytesTransferred)
{
   // This method is called to process an incomming message.
   //
   std::stringstream ss;
   int ByteCount;
   try
   {
       ss << "SSLSocket::HandleRead: From worker thread " << boost::this_thread::get_id() << ".\n";
      Log.LogString(ss.str(), LogInfo);
      // Set to exit this thread if the user is done.
      if (!ReqAlive)
      {
         // IOService->stop();
         return;
      }
      if (!error)
      {
         // Get the number of bytes in the message.
         if (bytesTransferred == 4)
         {
            ByteCount = BytesToInt(pDataBuf);
         }
         else
         {
            // Call the C# callback method that will handle the message.
            ss << "SSLSocket::HandleRead: From worker thread " << boost::this_thread::get_id() << "; # bytes transferred = " << bytesTransferred << ".\n";
            Log.LogString(ss.str(), LogDebug2);
            Log.LogBuf(pDataBuf, (int)bytesTransferred, true, LogDebug3);
            Log.LogString("SSLSocket::HandleRead: sending msg to the C# client.\n\n", LogDebug2);
            CallbackFunction(this, bytesTransferred, (void*)pDataBuf);
            // Prepare to read in the next message length.
            ByteCount = MsgLenBytes;
         }
         pDataBuf = BufMang.GetPtr(ByteCount);
         boost::system::error_code Err;
         // boost::asio::async_read(pSocket, boost::asio::buffer(pDataBuf, ByteCount), boost::bind(&SSLSocket::HandleRead,
           //  this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         Locking CodeLock(SocketLock); // Single thread the code.
         boost::asio::async_read(*pSocket, boost::asio::buffer(pDataBuf, ByteCount), boost::bind(&SSLSocket::HandleRead,
            this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         // boost::asio::read(pSocket, boost::asio::buffer(reply_), boost::asio::transfer_exactly(ByteCount), Err);
      }
      else
      {
         Log.LogString("SSLSocket::HandleRead failed: " + error.message() + "\n", LogError);
         Stop();
      }
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::HandleRead: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}

If none of the above is helpful, then put in some debug code that logs all of the calls to a log file so that you can see what is going on. You might also want to consider downloading Wire Shark in order to see what data is going out and coming in.

Upvotes: 1

Related Questions