繁体   English   中英

提升全双工 - 只有客户端到服务器才能工作,消息从服务器到客户端

[英]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_pServerThreadm_pClientThread有何不同?

为什么connection声明两次,一次作为原始指针,一次作为 shared_ptr?

许多函数被遗漏了(连接?ClientThreadRunning?),变量(上面提到, ipiter突然出现)等。

总而言之,我停止尝试在一两个小时后编译示例:

在 Coliru 上破碎

#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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM