我有一些我想建立TCP客户端的软件。我不知道这是否是最好的架构,但在我的软件中,我产生了一个将用于网络I / O的线程。如果有更好的架构,我会很感激一些指示和建议。
两个线程都引用了boost :: asio :: io_service对象和一个封装套接字对象的Session对象。 sesson对象大致如下:
class Session
{
public:
Session(
boost::asio::io_service & io_service,
std::string const & ip_address,
std::string const & port)
: io_service_(io_service),
resolver_(io_service),
socket_(io_service),
ip_address_(ip_address),
port_(port),
{}
virtual void start();
virtual ~Session();
virtual void stop();
void write(std::string const & msg);
void handle_resolve(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr);
void handle_connect(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr);
void handle_close();
void handle_write(const boost::system::error_code & error);
private:
boost::asio::io_service & io_service_;
boost::asio::ip::tcp::resolver resolver_;
boost::asio::ip::tcp::socket socket_;
std::string ip_address_;
std::string port_;
};
在I / O线程运行循环中,调用会话对象的start()方法,该方法连接到服务器。 (这很有用,顺便说一句)。然后,线程位于循环中,调用I / O服务对象[io_service_.run()]上的run()方法以触发事件。
主线程在想要发送数据时调用会话的write()方法,会话对象使用要写入的数据调用boost :: async_write,然后调用作为会话对象成员的回调方法( handle_write)。
虽然我有I / O线程连接到服务器,但我无法触发handle_write方法。我已经验证主线程正在调用会话对象并在套接字上执行async_write()。只是回调永远不会被触发。我也没有在服务器端或使用tcpdump通过线路看到任何数据。
知道我的问题可能在哪里?有没有更好的方法来组织架构?最重要的是,我不想阻止执行I / O的主线程。
以下是从主线程生成io线程的代码(对于间距道歉):
boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
new Session::Session(
io_service,
std::string("127.0.0.1"),
std::string("17001")));
// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
boost::bind(
&Session::start,
session_ptr_.get()));
start()方法的代码如下:
void Session::start()
{
typedef boost::asio::ip::tcp tcp;
tcp::resolver::query query(
tcp::v4(),
ip_address_,
port_);
resolver_.async_resolve(
query,
boost::bind(
&Session::handle_resolve,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
while(1){ // improve this later
io_service_.run();
}
}
解析器的回调:
void Session::handle_resolve(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr)
{
if (!error)
{
boost::asio::ip::tcp::endpoint endpoint = *endpoint_itr;
socket_.async_connect(
endpoint,
boost::bind(
&Session::handle_connect,
this,
boost::asio::placeholders::error,
++endpoint_itr));
}
else
{
std::cerr << "Failed to resolve\n";
std::cerr << "Error: " << error.message() << std::endl;
}
}
connect的回调:
void Session::handle_connect(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr)
{
typedef boost::asio::ip::tcp tcp;
if (!error)
{
std::cerr << "Connected to the server!\n";
}
else if (endpoint_itr != tcp::resolver::iterator())
{
socket_.close();
socket_.async_connect(
*endpoint_itr,
boost::bind(
&Session::handle_connect,
this,
boost::asio::placeholders::error,
++endpoint_itr));
}
else
{
std::cerr << "Failed to connect\n";
}
}
主线程可以调用发送异步写入的write()方法。
void Session::write(
std::string const & msg)
{
std::cout << "Write: " << msg << std::endl;
boost::asio::async_write(
socket_,
boost::asio::buffer(
msg.c_str(),
msg.length()),
boost::bind(
&Session::handle_write,
this,
boost::asio::placeholders::error));
}
最后,写完成回调:
void Session::handle_write(
const boost::system::error_code & error)
{
if (error)
{
std::cout << "Write complete with errors !!!\n";
}
else
{
std::cout << "Write complete with no errors\n";
}
}
答案 0 :(得分:3)
看起来你的io服务在连接后会用完,之后你再次调用io_service :: run?看起来在while循环中调用run,但是我无法在任何地方看到重置调用。在再次调用同一个io_service上运行之前,需要调用io :: service :: reset。
在结构上,最好将work添加到io_service,然后您不需要在循环中调用它,并且一旦调用io_service :: stop就会退出运行。
答案 1 :(得分:2)
代码的这一部分
boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
new Session::Session(
io_service,
std::string("127.0.0.1"),
std::string("17001")));
// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
boost::bind(
&Session::start,
session_ptr_.get()));
对我来说是红旗。您的io_service
对象可能超出范围并导致奇怪的行为。 io_service
不可复制,因此将其作为非const引用传递给Session
可能不是您希望实现的。
samm@macmini ~> grep -C 2 noncopyable /usr/include/boost/asio/io_service.hpp
#include <boost/asio/detail/epoll_reactor_fwd.hpp>
#include <boost/asio/detail/kqueue_reactor_fwd.hpp>
#include <boost/asio/detail/noncopyable.hpp>
#include <boost/asio/detail/select_reactor_fwd.hpp>
#include <boost/asio/detail/service_registry_fwd.hpp>
--
*/
class io_service
: private noncopyable
{
private:
--
/// Class used to uniquely identify a service.
class io_service::id
: private noncopyable
{
public:
--
/// Base class for all io_service services.
class io_service::service
: private noncopyable
{
public:
如果您的代码基于HTTP client示例,则应注意io_service
内的main()
始终在范围内。作为Ralf pointed out,您的io_service
也可能在连接处理程序之后无法完成工作,这就是为什么你已经克服它来调用循环中的run()
while(1){ // improve this later
io_service_.run();
}
再次注意,HTTP client示例不会这样做。您需要在connect处理程序内部启动另一个异步操作,可以是读取或写入,具体取决于您的应用程序需要。