我正在尝试将一些现有代码转换为使用 boost 的 asio tcp 套接字而不是我们当前的实现。我能够从 boost 站点获得一个非常相似的示例 ( of a chat client/server ),但当我尝试将代码放入我自己的程序时,它停止工作。
我在做什么:
- 启动服务器进程
- 服务器进程创建一个空套接字并使用它来监听(使用 tcp::acceptor)端口(例如 10010)上的 TCP 连接
- 启动客户端进程
- 让客户端进程创建一个连接到服务器端口的套接字
- 当服务器看到客户端正在连接时,它开始在套接字上监听数据(使用 async_read)并创建另一个空套接字以监听端口上的另一个 TCP 连接
- 当客户端看到服务器已连接时,它会发送 100 字节的数据(使用 async_write)并等待套接字告诉它发送已完成...当发生这种情况时,它会打印一条消息并关闭<
- 当服务器收到已读取数据的通知时,它会打印一条消息并关闭
很明显,我已经从我试图实现的代码中大大缩减了这段代码,这是我可以制作的重现问题的尽可能小的代码。我在 Windows 上运行并且有一个 visual studio 解决方案文件,您可以 get .存在一些内存泄漏、线程安全问题等,但那是因为我正在从现有代码中取出一些东西,所以不要担心它们。
无论如何,这是一个带有一些常用内容的 header 、一个服务器和一个客户端的文件。
连接.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(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;
}
我搜索了一下,但运气不佳。据我所知,我几乎完全匹配样本,所以它一定是我忽略的小东西。
谢谢!
最佳答案
在您的客户端代码中,您的 ConnectHandler()
回调函数只是设置一个值然后返回,而不会向 io_service 发送任何更多工作。那时,async_connect()
操作是与 io_service 关联的唯一工作;所以当 ConnectHandler()
返回时,不再有与 io_service 相关的工作。因此,后台线程对 io_service.run()
的调用返回,线程退出。
一个可能的选择是从 ConnectHandler()
中调用 conn->AsyncReceive()
,以便 async_read()
获取在 ConnectHandler()
返回之前调用,因此后台线程对 io_service.run()
的调用不会返回。
另一种更简单的选择是在创建线程之前实例化一个 io_service::work 实例以调用 io_service::run(从技术上讲,您可以在任何点之前执行此操作到 io_service.run()
调用的返回):
...
// some point in the main() method, prior to creating the background thread
boost::asio::io_service::work work(io_service)
...
这在 io_service 文档中有记录:
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
关于boost::asio tcp async_read 永远不会返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2412520/