一码农 2022-08-08 11:13 采纳率: 68.8%
浏览 150
已结题

在使用boost::asio做TCP客户端断开后使用新线程重连后获取不到数据是怎么回事

在使用boost::asio做TCP客户端时,通过async_read_somey读取网络数据,第一次连上服务端后,可以正常读取服务端下发的数据,当服务端关闭后,async_read_some中也可以监听到事件,此时客户端类会通知它的调用者来重连服务端,此时问题来了,如果重边时不开新线程,就可以正常连上并收发数据,但是如果开了新线程,就只能连接成功,却收不到服务端下发的任何数据,也监听不到网络断开消息,不明白怎么回事,有了解的同学可以指导下吗,以下是代码,可直接运行,主要问题集中在Task::Release方法中

// TestAsio.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>

#include <iomanip>
#include <memory>
#include <array>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/bind/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>  
#include <boost/system/error_code.hpp>
#include <boost/thread.hpp>

#define BUFFER_SIZE 1024

class Cli;

class StateListener {
public:
    virtual void Release(int errCode) = 0;
};

class Cli {
public:
    Cli() = delete;
    Cli(const Cli&) = delete;
    Cli& operator=(const Cli&) = delete;
    Cli(boost::asio::io_service* io, StateListener* pInstance);
    ~Cli();

    void start();
    void stop();
private:
    void handle_read();
    bool m_bContinue;
    bool m_bConnectSuccess;
    StateListener* m_pInstance;
    boost::asio::io_service* m_io;
    boost::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
    boost::asio::deadline_timer m_connect_timer;
    unsigned char* m_pRecvBuf;
    const int m_iRecvBufLen;
};

Cli::Cli(boost::asio::io_service* io, StateListener* pInstance)
    : m_bContinue(false)
    , m_io(io)
    , m_pInstance(pInstance)
    , m_iRecvBufLen(BUFFER_SIZE)
    , m_bConnectSuccess(false)
    , m_connect_timer(*io)
{
    m_pRecvBuf = new unsigned char[BUFFER_SIZE];
}

Cli::~Cli()
{
    delete[] m_pRecvBuf;
}

void Cli::start()
{
    m_bContinue = true;

    boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 12345);
    boost::asio::ip::tcp::endpoint local_ep(boost::asio::ip::address::from_string("127.0.0.1"), 0);
    boost::shared_ptr<boost::asio::ip::tcp::socket> sock = boost::shared_ptr<boost::asio::ip::tcp::socket>(new boost::asio::ip::tcp::socket(*m_io));
    sock->open(boost::asio::ip::tcp::v4());
    sock->bind(local_ep);

    boost::system::error_code err;
    sock->async_connect(ep, [this, sock](const boost::system::error_code& error) {
        if (error)
        {
            std::cout << "async_connect err, " << error.message() << "\n";
            m_pInstance->Release(0);

            return;
        }
        std::cout << "connect success\n";
        m_bConnectSuccess = true;

        m_socket = sock;

        m_connect_timer.cancel();

        handle_read();
    });

    m_connect_timer.expires_from_now(boost::posix_time::seconds(5));
    m_connect_timer.async_wait([this](const boost::system::error_code& error) {
        if (!m_bConnectSuccess)
        {
            m_pInstance->Release(-1);
        }
    });
}

void Cli::stop()
{
    m_bContinue = false;

    if (m_socket == nullptr)
    {
        return;
    }
    if (m_socket->is_open())
    {
        try
        {
            m_socket->close();
            m_socket = nullptr;
        }
        catch (std::exception&)
        {
        }
    }
}

void Cli::handle_read()
{
    if (!m_bContinue)
    {
        return;
    }

    m_socket->async_read_some(boost::asio::buffer(m_pRecvBuf, m_iRecvBufLen),
        [this](const boost::system::error_code& error, size_t bytes_recved) {
            if (!error)
            {
                std::cout << "recv len " << bytes_recved << std::endl;//这里打印收到的数据

                handle_read();
            }
            else
            {
                if (m_pInstance)
                {
                    m_pInstance->Release(-2);//服务端断开后会从这里通知主程序
                }
            }
    });
}

class Task : public StateListener {
public:
    Task();
    ~Task();
    void Start();
    void Stop();

    virtual void Release(int errCode);
private:
    void RunCli();
    bool m_bContinue;
    std::thread* m_pReconnTrd;
    boost::asio::io_service m_io;
    std::shared_ptr<Cli> m_cli;
};

Task::Task()
    : m_cli(nullptr)
    , m_pReconnTrd(nullptr)
{

}

Task::~Task() {}

void Task::Start()
{
    m_bContinue = true;

    RunCli();

    new std::thread([this]() {
        m_io.run();
    });
}

void Task::Stop()
{
    m_bContinue = false;

    if (m_cli != nullptr) {
        m_cli->stop();
        m_cli = nullptr;
    }
    m_io.stop();
}

void Task::RunCli() {
    m_cli = std::shared_ptr<Cli>(new Cli(&m_io, this));
    m_cli->start();
}


/////***********************************
****** 重连逻辑在这里
****** 重连后收不到数据的问题也由这里引起
*********************************//////
void Task::Release(int err) {
    printf("call Task::Release, err=%d\n", err);
    if (m_cli != NULL) {
        m_cli->stop();
        m_cli = NULL;
    }
    if (m_pReconnTrd == NULL) {
        m_pReconnTrd = new std::thread([this]() {//如果在线程中重连,连上后就收不到数据
            int sec = 0;
            while (m_bContinue) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                printf("sec=%d\n", sec);
                if (++sec >= 10) {
                    m_pReconnTrd = NULL;
                    //new std::thread(&Task::RunCli, this);//此处也一样,如果开新线程,连上后也收不到数据
                    RunCli();
                    break;
                }
            }
            std::cout << "exit reconn thread\n";
        });
    }
}

int main()
{
    Task t;
    t.Start();
    char c;
    while ((c = getchar()) != 'q') {
        getchar();
    }
    t.Stop();

    return 0;
}
  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 8月16日
    • 创建了问题 8月8日

    悬赏问题

    • ¥15 关于温度改变石墨烯介电性能(关键词-介电常数)
    • ¥150 HDMI分路器LT86102 的输出在890MHz频点处EMC超标8DB
    • ¥15 druid(相关搜索:数据库|防火墙)
    • ¥15 大一python作业
    • ¥15 preLaunchTask"C/C++: aarch64- apple-darwin22-g++-14 生成活动 文件”已终止,退出代码为-1。
    • ¥60 如何鉴定微信小程序数据被篡改过
    • ¥18 关于#贝叶斯概率#的问题:这篇文章中利用em算法求出了对数似然值作为概率表参数,然后进行概率表计算,这个概率表是怎样计算的呀
    • ¥20 C#上传XML格式数据
    • ¥15 elementui上传结合oss接口断点续传,现在只差停止上传和继续上传,各大精英看下
    • ¥20 手机截图相片分辨率降低一半