[英]Boost full duplex - only client to server works, messages from server to client
我有一個提升 asio 項目,我對此束手無策。 我定義了一個 TCPConnect class,它由 TCPSession 和 TCPClient class 繼承。 這樣做的原因是因為我希望連接的服務器端和客戶端都直接用於通過全雙工 TCP/IP 發送和接收消息。 只有 TCPSession class 監聽新連接,只有 TCPClient class 進行傳出連接。
當客戶端連接到服務器時,它會發送握手消息並阻止響應。 服務器收到消息並返回握手確認。 在發送 ack 時,服務器認為連接已完成。 收到 ack 后,客戶端認為連接已完成。
我遇到的問題是只有客戶端(TCPClient 對象)可以調用其繼承的 TCPConnect::Send() 並讓它被遠程 TCPSession object 接收。 如果服務器端(TCPSession 對象)調用 TCPConnect::Send(),它會將消息放到線路上沒有問題,但遠程 TCPClient object 永遠不會收到消息。
我必須說我是使用 boost 的初學者。 我已經研究過這個問題,試圖對我的搜索查詢進行措辭和改寫,但沒有找到解決方案。 在雙方我都有 TCPSession 和 TCPClient 對象坐在 async_read() 上,但在客戶端,當服務器端發送消息時,不會調用 async_read() 的回調。 我假設這與 io_service 在 TCPSession 對象端的設置方式有關,並且可能與線程有關。
因此,TCPServer 在 TCPWorker::run() function 中啟動,這個 function 在自己的線程中運行。 TCPWorker class 擁有 io_service object。 線程化的 run() function 實例化一個 TCPServer object 然后調用 io_service::run()。 TCPServicer object 負責為新連接創建 TCPSession 對象。 對於每個新連接,TCPServer 使用 io_service 指針創建一個新的 TCPSession object 並在其接受器上調用 async_accept。 一旦接受了新連接,TCPSession 的套接字就會設置為 async_read()。 所以可知io_service和可以創建使用的多個sockets在一個線程中。
在 TCPClient 端,當調用 Connect() 時,如果 io_service 不存在和/或 io_service 的線程尚不存在,則使用以下行創建它們:
if (:m_pClientServiceThread) { if (:m_pclient_io_service) // if no io_service has yet been created m_pclient_io_service = new boost::asio;:io_service: m_pClientServiceThread = new boost::thread(boost::bind(&boost::asio::io_service,;run, m_pclient_io_service)); }
所以服務正在一個線程中運行。 然后在給定 io_service 指針的情況下創建一個新的解析器和一個新的 boost::asio::ip::tcp::socket,並在給定套接字和有效解析端點的情況下調用 boost::asio::connect()。 所以客戶端似乎有它的 io_service 在一個線程中運行。 成功建立連接后,然后我使用 boost::asio::read() 發送握手消息,並坐在 boost::asio::read() 等待握手響應。 收到有效響應后,我將套接字傳遞給 async_read() 以等待傳入消息。
我已經看了很長時間了,但沒有弄清楚為什么客戶端 async_read() 沒有收到從服務器端發送的消息,即使服務器端接收到從客戶端發送的消息。
請幫我解決這個問題。 我很確定我沒有看到一些簡單的東西,但正如我所說,我不是提升專家,所以我不確定它是什么。
添加代碼:
TcpConnect class:
class TcpConnect : public boost::enable_shared_from_this<TcpConnect>
{
TcpConnect(boost::asio::io_service* pio_service, ConnType connType, std::string sHostIp, int iHostPort)
: m_pio_service(pio_service)
, eConnType(connType)
, m_strHostIp(sHostIp)
, m_iHostPort(iHostPort)
{
}
virtual ~TcpConnect() { /* does what is needed - this works */ }
bool SendBlocked(CmdPacket& msg, CmdPacket& rsp)
{
// Function used for handling connection handshake response
std::size_t write_length = boost::asio::write( *m_pSocket, boost::asio::buffer(msg.Serialize(), (std::size_t)msg.SerializedLength()));
if (msg.SerializedLength() != write_length)
{
return false;
}
boost::asio::streambuf sbuff;
boost::system::error_code error;
size_t reply_length(0);
// read header for message body length
byte* buffer = rsp.CreateBuffer(MSG_HEADER_LENGTH);
reply_length = boost::asio::read(*m_pSocket, boost::asio::buffer(buffer, MSG_HEADER_LENGTH), boost::asio::transfer_exactly(MSG_HEADER_LENGTH), error);
if (error || !rsp.ReadMessageLength())
{
/* error handled here */
return false;
}
// read message body
int expectedlen = rsp.BodyLength();
buffer = rsp.CreateBuffer(expectedlen);
reply_length = boost::asio::read(*m_pSocket, boost::asio::buffer(buffer, expectedlen), boost::asio::transfer_exactly(expectedlen), error);
if (error)
{
/* error handled here */
return false;
}
if (!rsp.Deserialize() || reply_length != rsp.BodyLength())
{
/* error handled here */
return false;
}
return true;
}
bool Send(CmdPacket& msg)
{
bool bStatus = true;
size_t write_length = 0;
try
{
write_length = boost::asio::write( *m_pSocket, boost::asio::buffer(msg.Serialize(), (std::size_t)msg.SerializedLength()) );
}
catch (...)
{
/* error handled here */
return false;
}
if (write_length != msg.SerializedLength())
{
/* error handled here */
return false;
}
return true;
}
void StartAsyncRead()
{
m_pInMsg = new CmdPacket();
boost::asio::async_read(*m_pSocket, boost::asio::buffer(m_pInMsg->CreateBuffer(MSG_HEADER_LENGTH), MSG_HEADER_LENGTH),
boost::bind(&TcpConnect::handle_read_header, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read_header(const boost::system::error_code& error, size_t bytes_transferred)
{
if (!error && bytes_transferred == MSG_HEADER_LENGTH && m_pInMsg->ReadMessageLength())
{
boost::asio::async_read(*m_pSocket,
boost::asio::buffer(m_pInMsg->CreateBuffer(m_pInMsg->SerializedLength()), m_pInMsg->SerializedLength()),
boost::bind(&TcpConnect::handle_read_body, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else if (error)
{
/* errors handled */
}
}
void handle_read_body(const boost::system::error_code& error, size_t bytes_transferred)
{
bool deserialized = false;
if (!error && (deserialized = m_pInMsg->Deserialize()))
{
// if not yet connected, expecting handshake message, in which case acknowledge, otherwise error
if (m_pInMsg->IsHandshake())
{
m_pInMsg->SetAcknowledge(true);
std::size_t write_length = boost::asio::write(
*m_pSocket, boost::asio::buffer(m_pInMsg->Serialize(), (std::size_t)m_pInMsg->SerializedLength()));
if (write_length == m_pInMsg->SerializedLength())
{
/* we sent the acknowledgement, so we consider we're connected */
}
else
{
/* handling error here */
return;
}
delete m_pInMsg;
m_pInMsg = NULL;
}
// if graceful disconnect, notify the connection manager of new status, which will remove the connection from the map
else if (m_pInMsg->IsDisconnect())
{
/* disconnect request handled here */
return;
}
else
{
/* message received, passing it to the local process here */
}
// set up to handle the next read
m_pInMsg = new CmdPacket;
boost::asio::async_read(*m_pSocket, boost::asio::buffer(m_pInMsg->CreateBuffer(MSG_HEADER_LENGTH), MSG_HEADER_LENGTH),
boost::bind(&TcpConnect::handle_read_header, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else if (error)
{
/* handle case where boost error */
}
else if (!deserialized)
{
/* handle case where message not correctly deserialized */
}
}
protected:
ConnType eConnType;
boost::asio::ip::tcp::socket* m_pSocket{ 0 };
boost::asio::io_service* m_pio_service;
boost::asio::ip::tcp::resolver::iterator m_itEndpoint;
CmdPacket* m_pInMsg{ 0 };
std::string m_strHostIp;
int m_iHostPort;
};
typedef boost::shared_ptr<TcpConnect> ConnectionPtr;
TcpClient class:
class TcpClient : public TcpConnect
{
public:
TcpClient(boost::asio::io_service& io_service, ConnType connType, const std::string& sIP, int iPort)
: TcpConnect(&io_service, connType, sIP, iPort)
{
}
~TcpClient() { /* does what is needed - this works */ }
//virtual ObjType Type() { return OT_CLIENT; }
//virtual int sessionId() { return -1; } // client end does not have a session id
//Use the following to initialize and to to reestablish the connection.
bool Connect()
{
bool bStatus = true;
//Convert the port to a string
std::stringstream ss;
ss << m_iHostPort;
std::string strPort = ss.str();
//Establish the connection
try
{
boost::system::error_code ec;
// create TCP resolver and query and resolve the endpoint
boost::asio::ip::tcp::resolver resolver(*m_pio_service);
boost::asio::ip::tcp::resolver::query query(m_strHostIp.c_str(), strPort.c_str());
boost::asio::ip::tcp::resolver::iterator m_iterEndpoint = resolver.resolve(query, ec);
if (ec)
{
/* error handled here */
bStatus = false;
}
else
{
// close an old socket (shouldn't ever be the case)
if (m_pSocket != NULL) CloseSocket(); /* NOTE: this is defined in TcpConnect, but not shown here */
// create the socket on the io_service object and connect to the endpoint
m_pSocket = new boost::asio::ip::tcp::socket(*m_pio_service);
boost::asio::connect(*m_pSocket, m_iterEndpoint, ec);
if (ec)
{
/* error handled here */
bStatus = false;
}
}
} //end try
catch(...)
{
/* error handled here */
bStatus = false;
}
return bStatus;
}
};
typedef boost::shared_ptr<TcpClient> TcpClientPtr;
TcpServer class(由 TcpWorker 運行並創建 TcpSession 對象):
class TcpServer;
class TcpSession : public TcpConnect
{
public:
TcpSession(boost::asio::io_service& io_service)
: TcpConnect(&io_service)
, m_session_id(next_session_id())
, m_pSocket(new tcp::socket(io_service))
{
}
virtual ~TcpSession() { /* NOTE: m_pSocket is owned and deleted by TcpConnect */ }
private:
int next_session_id()
{
static int id = 0;
return (++id > 0) ? id : 1;
}
private:
int m_session_id;
};
typedef boost::shared_ptr<TcpSession> TcpSessionPtr;
class TcpServer
{
public:
TcpServer(boost::asio::io_service& io_service, short port)
: m_pio_service(&io_service)
, m_acceptor(io_service, tcp::endpoint(tcp::v4(), port))
{
m_acceptor.listen();
}
~TcpServer()
{
boost::system::error_code errorcode;
m_acceptor.close(errorcode);
}
void start_accept()
{
TcpSessionPtr new_session(new TcpSession(*m_pio_service));
// start listening for this session
m_acceptor.async_accept(new_session->socket(),
boost::bind(&TcpServer::handle_accept, this, new_session,
boost::asio::placeholders::error));
new_session.reset();
}
private:
void handle_accept(TcpSessionPtr new_session, const boost::system::error_code& error)
{
if (!error)
{
new_session->StartAsyncRead(); /* NOTE: there is code for aggregating session objects */
/* NOTE: The result of an async_read() will be handled in TcpConnect::handle_read_header() */
}
else
{
/* error handled here */
}
// listen for the next connection
start_accept();
}
private:
boost::asio::io_service* m_pio_service;
tcp::acceptor m_acceptor;
};
class TcpWorker
{
public:
TcpWorker(unsigned int port)
: m_port(port)
{}
~TcpWorker() {}
void StopWorker()
{
if (!m_io_service.stopped())
{
m_io_service.stop();
while (!m_io_service.stopped()) { boost::this_thread::sleep(boost::posix_time::milliseconds(1)); }
}
}
void operator()() // threaded run function started from Communicator::Listen()
{
TcpServer server(m_io_service, (short)m_port);
server.start_accept(); // set up async_accept() for listening
std::size_t inumhandlers = m_io_service.run(); // blocks here until StopWorker() is called
}
private:
unsigned int m_port;
boost::asio::io_service m_io_service;
bool m_running;
};
通訊器 class:
class Communicator {
public:
Communicator() = default;
~Communicator() { /* does what is needed - this works */ }
bool Listen()
{
if (!m_pServerThread || !m_pServerWorker)
{
m_pServerWorker = new TcpWorker(m_myPort);
m_pServerThread = new boost::thread(&TcpWorker::operator(), m_pServerWorker);
return true;
}
return false;
}
bool Connect(int srcId, int destId, std::string ipaddr, int port)
{
bool ret = false;
if (connected(destId))
{
ret = true;
}
else
{
// if io_service is not running, start it (happens if never started, or if no remaining client sockets running)
if (!ClientThreadRunning())
{
if (m_pClientThread) // since going to create a new thread, make sure this one is deleted if exists
delete m_pClientThread;
if (!m_pclient_io_service) // if no io_service has yet been created
m_pclient_io_service = new boost::asio::io_service;
m_pClientServiceThread = new boost::thread(boost::bind(&boost::asio::io_service::run, m_pclient_io_service));
}
// create the connection. Wait for Ack before returning.
TcpClientPtr client(new TcpClient(*m_pclient_io_service, destId, ip, port));
// connect to the client and do a handshake
if (client->Connect())
{
// if an initial handshake works, we're connected
CmdPacket msg(CMD_NONE, srcId, destId, port, ipaddr), rsp;
msg.SetHandshake(); // this starts the handshake protocol, which is completed on receiving the necessary response.
if (!client->SendBlocked(msg, rsp) || rsp != msg)
{
client.reset();
ret = false;
}
else
{
// Connected, now set up for asynchronous reading
client->StartAsyncRead(m_pclient_io_service);
// save it in the class
connection = client;
ret = true;
}
}
// decrement reference count, if not added to shared pointer map, this will set client object for deletion
client.reset();
}
return ret;
}
bool sendMessage(CmdPacket& msg)
{
bool bret = false;
if (connection != nullptr)
{
iter->second->Send(msg);
}
return bret;
}
private:
TcpConnect *connection{ 0 };
TcpWorker* m_pServerWorker{ 0 };
boost::thread *m_pServerThread{ 0 };
boost::thread* m_pClientThread{ 0 };
boost::asio::io_service* m_pclient_io_service{ 0 };
ConnectionPtr connection{ 0 };
};
您的代碼不是獨立的。 它也是不同步的(TcpConnect 構造函數與初始值設定項列表不匹配,使用參數調用AsyncStartRead
)。 看起來您可能在 TcpSession 和 TcpConnect 類中重復了m_pSocket
成員,即:
很矛盾。
對混淆所有權和生命周期的指針存在不健康的依賴。
如果你真的啟用了編譯器警告,編譯器會告訴你幾個成員變量沒有按照你期望的順序初始化(基於構造函數的初始化列表中的順序)。
存在競爭條件的代碼氣味(使用 boolean 檢查循環睡眠?)。 未使用的變量( m_running
?)。
名稱非常混亂的未聲明變量(什么是m_pClientServiceThread
?它與m_pServerThread
和m_pClientThread
有何不同?
為什么connection
聲明兩次,一次作為原始指針,一次作為 shared_ptr?
許多函數被遺漏了(連接?ClientThreadRunning?),變量(上面提到, ip
, iter
突然出現)等。
總而言之,我停止嘗試在一兩個小時后編譯示例:
#undef NDEBUG // make sure we assert
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/endian/arithmetic.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <boost/thread.hpp>
namespace json = boost::json;
using byte = char;
using boost::asio::ip::tcp;
enum class ConnType {
Session
};
using Big32 = boost::endian::big_uint32_t;
static constexpr size_t MSG_HEADER_LENGTH = 2 * sizeof(Big32);
enum CmdType {
CMD_NONE,
CMD_HANDSHAKE,
CMD_ACK,
CMD_DISCONNECT,
};
struct CmdPacket {
std::string _buf;
json::value _data; // { "cmd_type": MsgType, "value": json::value }
bool IsHandshake() const { return _data.at("cmd_type") == CMD_HANDSHAKE; }
bool IsDisconnect() const{ return _data.at("cmd_type") == CMD_DISCONNECT; }
void SetAcknowledge(json::value v) {
auto& o = _data.as_object();
o["cmd_type"] = CMD_ACK;
o["value"] = v;
}
void SetHandshake() { _data.as_object()["cmd_type"] = CMD_HANDSHAKE; }
byte* CreateBuffer(uint32_t n) { _buf.assign(n, '\0'); return _buf.data(); }
uint32_t ReadMessageLength() const {
assert(_buf.size() >= sizeof(Big32));
boost::endian::big_uint32_t decode;
memcpy(&decode, _buf.data(), sizeof(decode));
return decode;
}
uint32_t BodyLength() const {
return ReadMessageLength();
}
std::string Serialize() const { return json::serialize(_data); }
bool Deserialize() {
json::error_code ec;
_data = json::parse(_buf, ec);
return !ec;
}
size_t SerializedLength() const { return Serialize().length(); }
};
class TcpConnect : public boost::enable_shared_from_this<TcpConnect> {
public:
virtual ~TcpConnect()
{ /* does what is needed - this works */
}
auto& socket() { assert(m_pSocket); return *m_pSocket; }
auto& socket() const { assert(m_pSocket); return *m_pSocket; }
protected:
TcpConnect(boost::asio::io_service* pio_service, ConnType connType,
std::string sHostIp, uint16_t iHostPort)
: m_pio_service(pio_service)
, eConnType(connType)
, m_pSocket(new tcp::socket(*pio_service))
, m_strHostIp(sHostIp)
, m_iHostPort(iHostPort)
{
}
void CloseSocket() {
if (m_pSocket)
m_pSocket->close();
}
bool SendBlocked(CmdPacket& msg, CmdPacket& rsp)
{
// Function used for handling connection handshake response
std::size_t write_length = boost::asio::write(
*m_pSocket,
boost::asio::buffer(msg.Serialize(),
(std::size_t)msg.SerializedLength()));
if (msg.SerializedLength() != write_length) {
return false;
}
boost::asio::streambuf sbuff;
boost::system::error_code error;
size_t reply_length(0);
// read header for message body length
byte* buffer = rsp.CreateBuffer(MSG_HEADER_LENGTH);
reply_length = boost::asio::read(
*m_pSocket, boost::asio::buffer(buffer, MSG_HEADER_LENGTH),
boost::asio::transfer_exactly(MSG_HEADER_LENGTH), error);
if (error || !rsp.ReadMessageLength()) {
/* error handled here */
return false;
}
// read message body
int expectedlen = rsp.BodyLength();
buffer = rsp.CreateBuffer(expectedlen);
reply_length = boost::asio::read(
*m_pSocket, boost::asio::buffer(buffer, expectedlen),
boost::asio::transfer_exactly(expectedlen), error);
if (error) {
/* error handled here */
return false;
}
if (reply_length != rsp.BodyLength() || !rsp.Deserialize()) {
/* error handled here */
return false;
}
return true;
}
bool Send(CmdPacket& msg)
{
//bool bStatus = true;
size_t write_length = 0;
try {
write_length = boost::asio::write(
*m_pSocket,
boost::asio::buffer(msg.Serialize(),
(std::size_t)msg.SerializedLength()));
} catch (...) {
/* error handled here */
return false;
}
if (write_length != msg.SerializedLength()) {
/* error handled here */
return false;
}
return true;
}
public:
void StartAsyncRead()
{
m_pInMsg = new CmdPacket();
boost::asio::async_read(
*m_pSocket,
boost::asio::buffer(m_pInMsg->CreateBuffer(MSG_HEADER_LENGTH),
MSG_HEADER_LENGTH),
boost::bind(&TcpConnect::handle_read_header, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
void handle_read_header(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error && bytes_transferred == MSG_HEADER_LENGTH &&
m_pInMsg->ReadMessageLength()) {
boost::asio::async_read(
*m_pSocket,
boost::asio::buffer(
m_pInMsg->CreateBuffer(m_pInMsg->SerializedLength()),
m_pInMsg->SerializedLength()),
boost::bind(&TcpConnect::handle_read_body, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else if (error) {
/* errors handled */
}
}
void handle_read_body(const boost::system::error_code& error,
size_t bytes_transferred)
{
bool deserialized = false;
if (!error && (deserialized = m_pInMsg->Deserialize())) {
// if not yet connected, expecting handshake message, in which case
// acknowledge, otherwise error
if (m_pInMsg->IsHandshake()) {
m_pInMsg->SetAcknowledge(true);
std::size_t write_length = boost::asio::write(
*m_pSocket,
boost::asio::buffer(
m_pInMsg->Serialize(),
(std::size_t)m_pInMsg->SerializedLength()));
if (write_length == m_pInMsg->SerializedLength()) {
/* we sent the acknowledgement, so we consider we're
* connected */
} else {
/* handling error here */
return;
}
delete m_pInMsg;
m_pInMsg = NULL;
}
// if graceful disconnect, notify the connection manager of new
// status, which will remove the connection from the map
else if (m_pInMsg->IsDisconnect()) {
/* disconnect request handled here */
return;
} else {
/* message received, passing it to the local process here */
}
// set up to handle the next read
m_pInMsg = new CmdPacket;
boost::asio::async_read(
*m_pSocket,
boost::asio::buffer(m_pInMsg->CreateBuffer(MSG_HEADER_LENGTH),
MSG_HEADER_LENGTH),
boost::bind(&TcpConnect::handle_read_header, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else if (error) {
/* handle case where boost error */
} else if (!deserialized) {
/* handle case where message not correctly deserialized */
}
}
protected:
boost::asio::io_service* m_pio_service;
ConnType eConnType;
boost::asio::ip::tcp::socket* m_pSocket{0};
boost::asio::ip::tcp::resolver::iterator m_itEndpoint;
CmdPacket* m_pInMsg{0};
std::string m_strHostIp;
uint16_t m_iHostPort;
};
typedef boost::shared_ptr<TcpConnect> ConnectionPtr;
// TcpClient class:
class TcpClient : public TcpConnect {
public:
TcpClient(boost::asio::io_service& io_service, ConnType connType,
const std::string& sIP, uint16_t iPort)
: TcpConnect(&io_service, connType, sIP, iPort)
{
}
~TcpClient()
{ /* does what is needed - this works */
}
// virtual ObjType Type() { return OT_CLIENT; }
// virtual int sessionId() { return -1; } // client end does not have a
// session id
// Use the following to initialize and to to reestablish the connection.
bool Connect()
{
bool bStatus = true;
// Convert the port to a string
std::stringstream ss;
ss << m_iHostPort;
std::string strPort = ss.str();
// Establish the connection
try {
boost::system::error_code ec;
// create TCP resolver and query and resolve the endpoint
boost::asio::ip::tcp::resolver resolver(*m_pio_service);
boost::asio::ip::tcp::resolver::query query(m_strHostIp.c_str(),
strPort.c_str());
boost::asio::ip::tcp::resolver::iterator m_iterEndpoint =
resolver.resolve(query, ec);
if (ec) {
/* error handled here */
bStatus = false;
} else {
// close an old socket (shouldn't ever be the case)
if (m_pSocket != NULL)
CloseSocket(); /* NOTE: this is defined in TcpConnect, but
not shown here */
// create the socket on the io_service object and connect to the
// endpoint
m_pSocket = new boost::asio::ip::tcp::socket(*m_pio_service);
boost::asio::connect(*m_pSocket, m_iterEndpoint, ec);
if (ec) {
/* error handled here */
bStatus = false;
}
}
} // end try
catch (...) {
/* error handled here */
bStatus = false;
}
return bStatus;
}
};
typedef boost::shared_ptr<TcpClient> TcpClientPtr;
// TcpServer class (run by TcpWorker and creates TcpSession objects):
class TcpServer;
class TcpSession : public TcpConnect {
public:
TcpSession(boost::asio::io_service& io_service)
: TcpConnect(&io_service, ConnType::Session, "127.0.0.1", 8787)
, m_session_id(next_session_id())
{
}
virtual ~TcpSession()
{ /* NOTE: m_pSocket is owned and deleted by TcpConnect */
}
private:
int next_session_id()
{
static int id = 0;
return (++id > 0) ? id : 1;
}
private:
int m_session_id;
};
typedef boost::shared_ptr<TcpSession> TcpSessionPtr;
class TcpServer {
public:
TcpServer(boost::asio::io_service& io_service, uint16_t port)
: m_pio_service(&io_service)
, m_acceptor(io_service, tcp::endpoint(tcp::v4(), port))
{
m_acceptor.listen();
}
~TcpServer()
{
boost::system::error_code errorcode;
m_acceptor.close(errorcode);
}
void start_accept()
{
TcpSessionPtr new_session(new TcpSession(*m_pio_service));
// start listening for this session
m_acceptor.async_accept(new_session->socket(),
boost::bind(&TcpServer::handle_accept, this,
new_session,
boost::asio::placeholders::error));
new_session.reset();
}
private:
void handle_accept(TcpSessionPtr new_session,
const boost::system::error_code& error)
{
if (!error) {
new_session->StartAsyncRead(); /* NOTE: there is code for
aggregating session objects */
/* NOTE: The result of an async_read() will be handled in
* TcpConnect::handle_read_header() */
} else {
/* error handled here */
}
// listen for the next connection
start_accept();
}
private:
boost::asio::io_service* m_pio_service;
tcp::acceptor m_acceptor;
};
class TcpWorker {
public:
TcpWorker(uint16_t port) : m_port(port) {}
~TcpWorker() {}
void StopWorker()
{
if (!m_io_service.stopped()) {
m_io_service.stop();
while (!m_io_service.stopped()) {
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
}
}
void
operator()() // threaded run function started from Communicator::Listen()
{
TcpServer server(m_io_service, m_port);
server.start_accept(); // set up async_accept() for listening
std::size_t inumhandlers =
m_io_service.run(); // blocks here until StopWorker() is called
}
private:
uint16_t m_port;
boost::asio::io_service m_io_service;
bool m_running;
};
// Communicator class:
class Communicator {
public:
uint16_t m_myPort = 8787;
Communicator() = default;
~Communicator()
{ /* does what is needed - this works */
}
bool Listen()
{
if (!m_pServerThread || !m_pServerWorker) {
m_pServerWorker = new TcpWorker(m_myPort);
m_pServerThread =
new boost::thread(&TcpWorker::operator(), m_pServerWorker);
return true;
}
return false;
}
bool Connect(int srcId, int destId, std::string ipaddr, uint16_t port)
{
bool ret = false;
if (connected(destId)) {
ret = true;
} else {
// if io_service is not running, start it (happens if never started,
// or if no remaining client sockets running)
if (!ClientThreadRunning()) {
if (m_pClientThread) // since going to create a new thread, make
// sure this one is deleted if exists
delete m_pClientThread;
if (!m_pclient_io_service) // if no io_service has yet been
// created
m_pclient_io_service = new boost::asio::io_service;
m_pClientServiceThread = new boost::thread(boost::bind(
&boost::asio::io_service::run, m_pclient_io_service));
}
// create the connection. Wait for Ack before returning.
TcpClientPtr client(
new TcpClient(*m_pclient_io_service, destId, ip, port));
// connect to the client and do a handshake
if (client->Connect()) {
// if an initial handshake works, we're connected
CmdPacket msg(CMD_NONE, srcId, destId, port, ipaddr), rsp;
msg.SetHandshake(); // this starts the handshake protocol, which
// is completed on receiving the necessary
// response.
if (!client->SendBlocked(msg, rsp) || rsp != msg) {
client.reset();
ret = false;
} else {
// Connected, now set up for asynchronous reading
client->StartAsyncRead(m_pclient_io_service);
// save it in the class
connection = client;
ret = true;
}
}
// decrement reference count, if not added to shared pointer map,
// this will delete client object
client.reset();
}
return ret;
}
bool sendMessage(CmdPacket& msg)
{
bool bret = false;
if (connection != nullptr) {
iter->second->Send(msg);
}
return bret;
}
private:
ConnectionPtr connection;
TcpWorker* m_pServerWorker{0};
boost::thread* m_pServerThread{0};
boost::thread* m_pClientThread{0};
boost::asio::io_service* m_pclient_io_service{0};
};
int main() {}
所有這些都讓我想起了這個答案: 如何使這個 HTTPS 連接在 Beast 中持久存在? 在那個答案中,我展示了如何為不同的目的地保留一個連接池。 它使用了不同的風格,所以希望你能以此為靈感。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.