异步服务端写数据没问题,读数据总是失败。网上的异步服务端的例子要么是只有写的,要么是分为两个类的。我这个例子,accept和读写封装在一个类总是失败。
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <unistd.h>
#include <boost/smart_ptr/scoped_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <bits/shared_ptr.h>
#include <boost/smart_ptr/enable_shared_from_this.hpp>
using namespace boost::asio;
class TcpServer : public boost::enable_shared_from_this<TcpServer> {
public:
TcpServer(int port) : m_iosvc(), m_acpt(m_iosvc, ip::tcp::endpoint(ip::tcp::v4(), port)) {
}
void onService() {
startAccept();
m_iosvc.run();
}
private:
void startAccept() {
// ip::tcp::socket* newskt = new ip::tcp::socket(m_iosvc);
boost::shared_ptr<ip::tcp::socket> newskt = boost::make_shared<ip::tcp::socket>(m_iosvc);
m_acpt.async_accept(*newskt, boost::bind(&TcpServer::accept_handler, this, newskt, placeholders::error));
}
void accept_handler(boost::shared_ptr<ip::tcp::socket> skt, const boost::system::error_code& error) {
if (error) {
std::cout << "error: " << error.message() << std::endl;
m_iosvc.stop();
return;
}
startAccept();
std::cout << "connection arrived: " << skt->remote_endpoint().address() << std::endl;
char* readbuf = new char[3];
boost::shared_ptr<std::string> responseMsg = boost::make_shared<std::string>("ok");
// skt->async_write_some(buffer(*responseMsg),
// boost::bind(&TcpServer::write_handler, this, responseMsg, placeholders::error, placeholders::bytes_transferred));
// async_read(*skt, buffer(readbuf, 2),
// boost::bind(&TcpServer::read_handler, this, readbuf, placeholders::error, placeholders::bytes_transferred));
skt->async_read_some(buffer(readbuf, 2),
boost::bind(&TcpServer::read_handler, shared_from_this(), readbuf, placeholders::error, placeholders::bytes_transferred));
}
void write_handler(boost::shared_ptr<std::string> pstr,
boost::system::error_code ec, size_t bytes_transferred) {
if (ec)
std::cout << "send fail!" << std::endl;
else
std::cout << *pstr << " sent" << std::endl;
}
void read_handler(char* readbuf, const boost::system::error_code& error, std::size_t bytes_transferred) {
if (error.value() != boost::system::errc::operation_canceled) {
std::cout << "read_handler error: " << error.message() << std::endl;
m_iosvc.stop();
return;
}
if (bytes_transferred != 2) {
std::cout << "bytes_transferred: " << bytes_transferred << std::endl;
m_iosvc.stop();
return;
}
std::cout << "data: " << *readbuf << std::endl;
}
io_service m_iosvc;
ip::tcp::acceptor m_acpt;
};
int main(int argc, char* argv[]) {
TcpServer server(9527);
server.onService();
return 0;
}
用下面中方式,异常为 operation canceled
skt->async_read_some(buffer(readbuf, 2),
boost::bind(&TcpServer::read_handler, this, readbuf, placeholders::error, placeholders::bytes_transferred));
看到stack overflow上有人说应该把 this 换成 shared_from_this() ,就像我现在代码的写法,错误是 tr1::bad_weak_ptr。
接下来我将像网上的例子那样把这个类拆分为两个类,一个处理accept,一个处理读写。
更新:
经过与网上拆分为两个类的例子对比发现,区别在于startAccept()中创建并交给shared_ptr托管的ip::tcp::socket类型变量newskt的生命周期。
网上拆分为两个类的例子中,在调用async_accept前创建socket类型变量skt,被由负责读写的connection类保管,只有在connection释放即读写完成后skt才会被释放。而我的例子中,socket类型变量由shared_ptr托管,被传入TcpServer::accept_handler中,它将在TcpServer::accept_handler函数返回后被释放。而TcpServer::accept_handler函数内调用async_read,并设置读事件回调TcpServer::read_handler。由此,尽管读事件回调TcpServer::read_handler在我这层代码上并没有直接用到ip::tcp::socket类型变量,但是读事件内部必须依赖socket类型变量,否则就会像我的例子里socket类型变量提前释放,导致异常operation canceled。