boost::asio tcp async_read never returns
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:
- Start a server process
- 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)
- Start a client process
- Have the client process create a socket connect to the server's port
- 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
- 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
- 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. 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
#include
#include
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(ct->GetSize())), tip);
boost::asio::async_write(sock_, boost::asio::buffer(ct->GetBuffer(),
static_cast(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(ct->GetSize())), tip);
boost::asio::async_read(sock_, boost::asio::buffer(ct->GetBuffer(),
static_cast(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
#include
#include
#include
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(开发者_C百科boost::posix_time::millisec(1));
}
std::cout (angle brackets here) "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 (angle brackets here) "Done\n";
return 0;
}
BoostConnectionServer.cpp:
#include "Connection.hpp"
#include
#include
#include
#include
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 (angle brackets here) "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 (angle brackets here) "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.
Thanks!
In your client code, your ConnectHandler()
callback function just sets a value and then returns, without posting any more work to the io_service. At that point, that async_connect()
operation is the only work associated with the io_service; so when ConnectHandler()
returns, there is no more work associated with the io_service. Thus the background thread's call to io_service.run()
returns, and the thread exits.
One potential option would be to call conn->AsyncReceive()
from within ConnectHandler()
, so that the async_read()
gets called prior to the ConnectHandler()
returning and thus the background thread's call to io_service.run()
won't return.
Another option, the more trivial one, would be to instantiate an io_service::work instance prior to creating your thread to call io_service::run (technically, you could do this at any point prior to the io_service.run()
call's returning):
...
// some point in the main() method, prior to creating the background thread
boost::asio::io_service::work work(io_service)
...
This is documented in the io_service documentation:
Stopping the io_service from running out of work
Some applications may need to prevent an io_service object's run() call from returning when there is no more work to do. For example, the io_service may be being run in a background thread that is launched prior to the application's asynchronous operations. The run() call may be kept running by creating an object of type io_service::work:
http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/reference/io_service.html
精彩评论