C++ asio如何从客户端异步读取数据并定期写入数据(如果有的话)
我是boost::asio的初学者,请帮助我 我需要写单线程TCP服务器。服务器应接受客户端连接,并持续从客户端套接字读取输入数据。服务器应定期向客户端发送数据。所以我有一个问题——所有的例子都描述了当我们总是有循环时的情况C++ asio如何从客户端异步读取数据并定期写入数据(如果有的话),c++,boost,boost-asio,C++,Boost,Boost Asio,我是boost::asio的初学者,请帮助我 我需要写单线程TCP服务器。服务器应接受客户端连接,并持续从客户端套接字读取输入数据。服务器应定期向客户端发送数据。所以我有一个问题——所有的例子都描述了当我们总是有循环时的情况 异步接收() on_receive()->async_write() on_write()->转到1:) 所以我的决定是使用定时器来检查要发送到套接字的数据 我编写了测试服务器,有一个非常奇怪的行为——如果客户端连接,做一些事情,并在一段时间内一个接一个地断开连接,那么工作
#include <boost/none.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
using namespace boost::asio;
using namespace boost::posix_time;
class CIncommingConnection ;
typedef boost::shared_ptr<CIncommingConnection> CIncommingConnectionPtr;
struct IIncomingServer
{
virtual void OnData(CIncommingConnectionPtr pConn, const char *pData, size_t bytes) = 0;
virtual void OnConnected(CIncommingConnectionPtr pConn) = 0;
virtual void OnDisconnected(const boost::system::error_code& err, CIncommingConnectionPtr pConn) = 0;
};
class CAutoLock
{
public:
CAutoLock(CRITICAL_SECTION &cs) :
m_cs(cs)
{
::EnterCriticalSection(&m_cs);
}
~CAutoLock()
{
::LeaveCriticalSection(&m_cs);
}
private:
CRITICAL_SECTION &m_cs;
};
class CIncommingConnection : public boost::enable_shared_from_this<CIncommingConnection>
,boost::noncopyable
{
public:
CIncommingConnection(const std::string sPeerName, boost::asio::io_service &service, IIncomingServer *pServer) :
m_service(service)
,sock_(service)
,m_sPeerName(sPeerName)
,m_pServer(pServer)
,m_timer(service)
{
::InitializeCriticalSection(&m_cs);
std::cout << "CIncommingConnection()" << std::endl ;
}
~CIncommingConnection()
{
std::cout << "CIncommingConnection()~" << std::endl ;
::DeleteCriticalSection(&m_cs);
}
ip::tcp::socket & sock()
{
return sock_;
}
void start()
{
m_pServer->OnConnected(shared_from_this());
do_read();
wait_for_outgoingdata();
}
private:
void stop()
{
sock_.close();
m_timer.cancel();
}
void do_read()
{
sock_.async_receive(buffer(read_buffer_), boost::bind(&CIncommingConnection::handler_read, this, _1, _2) );
}
void do_error(const boost::system::error_code& error)
{
CIncommingConnectionPtr pConn = shared_from_this();
stop() ;
m_pServer->OnDisconnected(error, pConn);
}
void handler_read(const boost::system::error_code& error, std::size_t bytes)
{
if (error)
{
do_error(error);
return ;
}
CIncommingConnectionPtr pConn = shared_from_this() ;
m_pServer->OnData(pConn, read_buffer_, bytes);
do_read();
}
void wait_for_outgoingdata()
{
m_timer.expires_from_now( boost::posix_time::millisec( 100 ) );
m_timer.async_wait( boost::bind( &CIncommingConnection::on_output_queue_timer, this, _1 ) );
}
void on_output_queue_timer(const boost::system::error_code& error)
{
if (error == boost::asio::error::operation_aborted)
{
return ;
}
CAutoLock oLock(m_cs);
if (!m_sOutBuf.empty())
sock_.async_send(buffer(m_sOutBuf), boost::bind(&CIncommingConnection::handler_write, this, _1, _2) );
else
wait_for_outgoingdata();
}
void handler_write(const boost::system::error_code& error, std::size_t bytes)
{
if (error)
return ;
if (bytes)
{
m_sOutBuf = m_sOutBuf.substr(bytes, m_sOutBuf.length()-bytes);
}
wait_for_outgoingdata();
}
private:
ip::tcp::socket sock_;
enum { max_msg = 1024 };
char read_buffer_[max_msg];
char write_buffer_[max_msg];
boost::asio::io_service &m_service ;
std::string m_sPeerName ;
std::string m_sOutBuf;
CRITICAL_SECTION m_cs ;
IIncomingServer *m_pServer;
boost::asio::deadline_timer m_timer;
};
class CIncomingServer : public boost::enable_shared_from_this<CIncomingServer>
, public IIncomingServer
, boost::noncopyable
{
public:
CIncomingServer(boost::asio::io_service &service,
unsigned int port,
bool bAllowManyConnections,
const std::string sPeerName) :
m_acceptor (service, ip::tcp::endpoint(ip::tcp::v4(), port), false)
,m_sPeerName(sPeerName)
,m_port(port)
,m_service(service)
,m_timer(service)
,m_bAllowManyConnections(bAllowManyConnections)
{
}
~CIncomingServer()
{
}
void run()
{
CIncommingConnectionPtr pConn (new CIncommingConnection(m_sPeerName, m_service, this));
m_clients.push_back( pConn );
m_acceptor.async_accept(pConn->sock(), boost::bind(&CIncomingServer::handle_accept, this, _1));
m_timer.expires_from_now( boost::posix_time::millisec( 500 ) );
m_timer.async_wait( boost::bind( &CIncomingServer::on_timer, this ) );
}
private:
void handle_accept(const boost::system::error_code & err)
{
m_clients.back()->start();
CIncommingConnectionPtr pConnNew (new CIncommingConnection(m_sPeerName, m_service, this));
m_clients.push_back( pConnNew );
m_acceptor.async_accept(pConnNew->sock(), boost::bind(&CIncomingServer::handle_accept, this, _1));
}
//IIncomingServer
virtual void OnData(CIncommingConnectionPtr pConn, const char *pData, size_t bytes)
{
std::cout << "Data received" << std::endl ;
}
virtual void OnConnected(CIncommingConnectionPtr pConn)
{
std::cout << "Client connected" << std::endl ;
}
virtual void OnDisconnected(const boost::system::error_code& err, CIncommingConnectionPtr pConn)
{
std::cout << "Client disconnected" << std::endl ;
auto it = std::find(m_clients.begin(), m_clients.end(), pConn) ;
if (it != m_clients.end())
{
m_clients.erase(it);
}
}
void on_timer()
{
//if (NeedTerminate())
//{
// m_service.stop();
// return ;
//}
m_timer.expires_from_now( boost::posix_time::millisec( 500 ) );
m_timer.async_wait( boost::bind( &CIncomingServer::on_timer, this ) );
}
private:
ip::tcp::acceptor m_acceptor ;
std::vector<CIncommingConnectionPtr> m_clients;
std::string m_sPeerName ;
unsigned int m_port ;
boost::asio::io_service &m_service ;
boost::asio::deadline_timer m_timer;
bool m_bAllowManyConnections;
};
int _tmain(int argc, _TCHAR* argv[])
{
boost::asio::io_service service ;
boost::shared_ptr<CIncomingServer> pServer;
try
{
pServer.reset( new CIncomingServer(service, 8000, false, "BS Server"));
pServer->run();
}
catch (const boost::system::system_error &err)
{
std::cout << "Error : " << err.what() << std::endl ;
return 0 ;
}
service.run();
return 0 ;
}
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间boost::asio;
使用名称空间boost::posix_time;
类连接;
typedef boost::shared_ptr CIncommingConnectionPtr;
结构IIncomingServer
{
虚拟void OnData(CIncommingConnectionPtr pConn,const char*pData,size_t bytes)=0;
连接的虚拟空位(CIncommingConnectionPtr pConn)=0;
虚拟连接无效(const boost::system::error\u code&err,CIncommingConnectionPtr pConn)=0;
};
尾锁类
{
公众:
尾锁(关键截面和cs):
m_cs(cs)
{
::EnterCriticalSection(&m_-cs);
}
~CAutoLock()
{
::离开关键部门(&m_-cs);
}
私人:
关键工段和机械控制系统;
};
类CIncommingConnection:public boost::从\u启用\u共享\u
,boost::不可复制
{
公众:
CIncommingConnection(const std::string sPeerName,boost::asio::io_服务和服务,IIncomingServer*pServer):
m_服务(服务)
,短袜(服务)
,m_sPeerName(sPeerName)
,m_pServer(pServer)
,m_定时器(服务)
{
::初始化CriticalSection(&m_-cs);
std::cout sock(),boost::bind(&CIncomingServer::handle_accept,this,_1));
m_timer.expires_from_now(boost::posix_time::毫秒(500));
异步等待(boost::bind(&CIncomingServer::on_timer,this));
}
私人:
无效句柄\u接受(const boost::system::error\u code&err)
{
m_clients.back()->start();
CIncommingConnectionPtr pConnNew(新的CIncommingConnection(m_sPeerName,m_service,this));
m_客户端。推回(PCONNEW);
异步接受(pConnNew->sock(),boost::bind(&CIncomingServer::handle_accept,this,_1));
}
//IIncomingServer
虚拟void OnData(CIncommingConnectionPtr pConn,const char*pData,size\u t字节)
{
std::cout长话短说:您应该将完成处理程序绑定到从\u this()
返回的共享\u ptr,而不是普通的this
(所谓的shared\u this
习惯用法)。这样可以确保正确自动管理连接对象的寿命
从技术上讲,现在会发生以下情况:do_error
导致发生两个操作:
CIncommingConnectionPtr
来自容器(同步
操作)在第(2)点,连接被破坏,因为没有其他的
共享\u ptr
持有它。现在…崩溃了!谢谢你的回答!看起来它是正确的,但我不能从中得出正确的结论:-(在从这个()传递共享\u之后)相反,这是异步处理程序。我的程序不再崩溃,但并不是所有CIncommingConnection实例都被破坏。看起来我错过了一些引用,对象仍然活着。我今天将尝试调查这种情况!非常感谢!@user1503944好吧,一看,我没有看到任何“永恒的”shared\u ptr
s出现在上面的代码中(即使在绑定到shared\u from\u this
)。也许,您的真实代码与您发布的代码不同。(顺便说一句,我建议您避免显式锁定缓冲区-相反,您可以将任何与缓冲区相关的代码发布到io\u服务
)@user1503944:简单看一下代码,就可以知道CIncommingConnection::on\u output\u queue\u timer()
调用链可能正在维护引用。即使timer.cancel(),调用链也可能会继续有关此行为的详细信息,请参阅备注。要解决此问题,请考虑在<代码> OnOutOutPuthQueLeGyTimeR()/<代码>中出错或如果<代码> Sokky.ISSUN OPEN()/代码>为false。@ Tanner Sansbury,但随着计时器取消,他调用“代码> Sokky.CuteleFor”<代码>,因此任何后续I/O在代码> Sokku无论如何都会失败。(并断开链)。@IgorR.wait_-for_-outgoingdata()
链仅在m_-sOutBuf
不为空时尝试对sock\u进行I/O操作;但是,m_-sOutBuf
在发布的代码中显示为空。当sock\u>打开时断开链()
is false似乎是一个合理的谓词,可以保证在调用stop()
后链断开。-1.这是一个吗?我怀疑。对不起,Abyx-但我不同意你的怀疑。例如,你认为在这个示例中,什么样的细节不重要?但感谢你的批评!