Boost logo

Boost Users :

Subject: [Boost-users] asio tcp async_read never returns
From: Tom Kent (lists_at_[hidden])
Date: 2010-03-09 16:18:07


I am trying to convert some existing code to use boost's asio tcp
sockets instead of our current implementation. I am able to get a very
similar example (of a chat client/server) from the boost site working,
but when I attempt to put the code into my own program it stops working.

What I am doing:

   1. Start a server process
   2. The server process makes an empty socket and uses it to listen
(with a tcp::acceptor) for TCP connections on a port (10010 for example)
   3. Start a client process
   4. Have the client process create a socket connect to the server's port
   5. When the server sees a client is connecting, it starts listening
for data(with async_read) on the socket and creates another empty socket
to listen for another TCP connection on the port
   6. When the client sees that the server has connected, it sends 100
bytes of data (with async_write) and waits for the socket to tell it the
send is finished...when that happens it prints a message and shuts down
   7. When the server gets notified that its has data that has been
read, it prints a message and shuts down

Obviously, I have greatly trimmed this code down from what I'm trying to
implement, this is as small as I could make something that reproduces
the problem. I'm running on windows and have a visual studio solution
file you can get (http://boost.teeks99.com/BoostConnectionTest.zip).
There's some memory leaks, thread safety problems, and such, but that's
because I'm taking stuff out of existing code, so don't worry about them.

Anyway, here's the files one header with some common stuff, a server,
and a client.

Connection.hpp:
#ifndef CONNECTION_HPP
#define CONNECTION_HPP
#include <boost/system/error_code.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

class ConnectionTransfer
{
public:
   ConnectionTransfer(char* buffer, unsigned int size) :
      buffer_(buffer), size_(size) {
   }
   virtual ~ConnectionTransfer(void){}

   char* GetBuffer(){return buffer_;}
   unsigned int GetSize(){return size_;}

   virtual void CallbackForFinished() = 0;

protected:
   char* buffer_;
   unsigned int size_;
};

class ConnectionTransferInProgress
{
public:
   ConnectionTransferInProgress(ConnectionTransfer* ct):
      ct_(ct)
   {}
   ~ConnectionTransferInProgress(void){}

   void operator()(const boost::system::error_code& error){Other(error);}
   void Other(const boost::system::error_code& error){
      if(!error)
         ct_->CallbackForFinished();
   }
private:
   ConnectionTransfer* ct_;
};

class Connection
{
public:
   Connection(boost::asio::io_service& io_service):
   sock_(io_service)
   {}
   ~Connection(void){}
   void AsyncSend(ConnectionTransfer* ct){
      ConnectionTransferInProgress tip(ct);
      //sock_->async_send(boost::asio::buffer(ct->GetBuffer(),
      // static_cast<std::size_t>(ct->GetSize())), tip);
      boost::asio::async_write(sock_, boost::asio::buffer(ct->GetBuffer(),
         static_cast<std::size_t>(ct->GetSize())), boost::bind(
         &ConnectionTransferInProgress::Other, tip,
boost::asio::placeholders::error));
   }
   void AsyncReceive(ConnectionTransfer* ct){
      ConnectionTransferInProgress tip(ct);
      //sock_->async_receive(boost::asio::buffer(ct->GetBuffer(),
      // static_cast<std::size_t>(ct->GetSize())), tip);
      boost::asio::async_read(sock_, boost::asio::buffer(ct->GetBuffer(),
         static_cast<std::size_t>(ct->GetSize())), boost::bind(
         &ConnectionTransferInProgress::Other, tip,
boost::asio::placeholders::error));
   }

   boost::asio::ip::tcp::socket& GetSocket(){return sock_;}
private:
   boost::asio::ip::tcp::socket sock_;
};
#endif //CONNECTION_HPP

BoostConnectionClient.cpp:
#include "Connection.hpp"

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>

using namespace boost::asio::ip;

bool connected;
bool gotTransfer;

class FakeTransfer : public ConnectionTransfer
{
public:
   FakeTransfer(char* buffer, unsigned int size) :
ConnectionTransfer(buffer, size)
   {
   }
   void CallbackForFinished()
   {
      gotTransfer = true;
   }
};

void ConnectHandler(const boost::system::error_code& error)
{
   if(!error)
      connected = true;
}

int main(int argc, char* argv[])
{
   connected = false;
   gotTransfer = false;

   boost::asio::io_service io_service;

   Connection* conn = new Connection(io_service);

   tcp::endpoint ep(address::from_string("127.0.0.1"), 10011);
   conn->GetSocket().async_connect(ep, ConnectHandler);

   boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

   while(!connected)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   std::cout << "Connected\n";

   char data[100];
   FakeTransfer* ft = new FakeTransfer(data, 100);
   conn->AsyncReceive(ft);

   while(!gotTransfer)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }

   std::cout << "Done\n";

   return 0;
}

BoostConnectionServer.cpp:
#include "Connection.hpp"

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>

using namespace boost::asio::ip;

Connection* conn1;
bool conn1Done;
bool gotTransfer;
Connection* conn2;

class FakeAcceptor
{
public:
   FakeAcceptor(boost::asio::io_service& io_service, const
tcp::endpoint& endpoint)
      :
      io_service_(io_service),
      acceptor_(io_service, endpoint)
  {
      conn1 = new Connection(io_service_);
      acceptor_.async_accept(conn1->GetSocket(),
         boost::bind(&FakeAcceptor::HandleAccept, this, conn1,
         boost::asio::placeholders::error));
   }
   void HandleAccept(Connection* conn, const boost::system::error_code&
error)
   {
      if(conn == conn1)
         conn1Done = true;
      conn2 = new Connection(io_service_);
      acceptor_.async_accept(conn2->GetSocket(),
         boost::bind(&FakeAcceptor::HandleAccept, this, conn2,
         boost::asio::placeholders::error));
   }
   boost::asio::io_service& io_service_;
   tcp::acceptor acceptor_;
};

class FakeTransfer : public ConnectionTransfer
{
public:
   FakeTransfer(char* buffer, unsigned int size) :
ConnectionTransfer(buffer, size)
   {
   }
   void CallbackForFinished()
   {
      gotTransfer = true;
   }
};

int main(int argc, char* argv[])
{
   boost::asio::io_service io_service;
   conn1Done = false;
   gotTransfer = false;
   tcp::endpoint endpoint(tcp::v4(), 10011);
   FakeAcceptor fa(io_service, endpoint);
   boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

   while(!conn1Done)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   std::cout << "Accepted incoming connection\n";

   char data[100];
   FakeTransfer* ft = new FakeTransfer(data, 100);
   conn1->AsyncReceive(ft);

   while(!gotTransfer)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   std::cout << "Success!\n";
   return 0;
}

I've searched around a bit, but haven't had much luck. As far as I can
tell, I'm almost exactly matching the sample, so it must be something
small that I'm overlooking. Any help is much appreciated!

Tom

P.S.
cross posted to stack overflow: http://stackoverflow.com/questions/2412520/


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net