【问题标题】:boost::asio write: Broken pipeboost::asio 写:断管
【发布时间】:2016-06-24 07:21:11
【问题描述】:

我有一个处理新连接的TCP 服务器,当有一个新连接时,将创建两个线程(std::thread,分离)。

void Gateway::startServer(boost::asio::io_service& io_service, unsigned short port) {

    tcp::acceptor TCPAcceptor(io_service, tcp::endpoint(tcp::v4(), port));

    bool UARTToWiFiGatewayStarted = false;

    for (;;) { std::cout << "\nstartServer()\n";

        auto socket(std::shared_ptr<tcp::socket>(new tcp::socket(io_service)));

        /*!
         * Accept a new connected WiFi client.
         */
        TCPAcceptor.accept(*socket);

        socket->set_option( tcp::no_delay( true ) );

        // This will set the boolean `Gateway::communicationSessionStatus` variable to true.
        Gateway::enableCommunicationSession();

        // start one thread
        std::thread(WiFiToUARTWorkerSession, socket, this->SpecialUARTPort, this->SpecialUARTPortBaud).detach();


        // start the second thread
        std::thread(UARTToWifiWorkerSession, socket, this->UARTport, this->UARTbaud).detach();
    }
}

两个工作函数中的第一个看起来像这样(这里我使用共享套接字读取):

void Gateway::WiFiToUARTWorkerSession(std::shared_ptr<tcp::socket> socket, std::string SpecialUARTPort, unsigned int baud) {
    std::cout << "\nEntered: WiFiToUARTWorkerSession(...)\n";

    std::shared_ptr<FastUARTIOHandler> uart(new FastUARTIOHandler(SpecialUARTPort, baud));

    try {
        while(true == Gateway::communicationSessionStatus) { std::cout << "WiFi->UART\n";

            unsigned char WiFiDataBuffer[max_incoming_wifi_data_length];

            boost::system::error_code error;

            /*!
             * Read the TCP data.
             */
            size_t length = socket->read_some(boost::asio::buffer(WiFiDataBuffer), error);

            /*!
             * Handle possible read errors.
             */
            if (error == boost::asio::error::eof) {
                // this will set the shared boolean variable from "true" to "false", causing the while loop (from the both functions and threads) to stop.
                Gateway::disableCommunicationSession();
                break; // Connection closed cleanly by peer.
            }
            else if (error) {
                Gateway::disableCommunicationSession();
                throw boost::system::system_error(error); // Some other error.
            }

            uart->write(WiFiDataBuffer, length);
        }
    }
    catch (std::exception &exception) {
        std::cerr << "[APP::exception] Exception in thread: " << exception.what() << std::endl;
    }

    std::cout << "\nExiting: WiFiToUARTWorkerSession(...)\n";
}

第二个(这里我使用线程共享套接字编写):

void Gateway::UARTToWifiWorkerSession(std::shared_ptr<tcp::socket> socket, std::string UARTport, unsigned int baud) {
    std::cout << "\nEntered: UARTToWifiWorkerSession(...)\n";

    /*!
     * Buffer used for storing the UART-incoming data.
     */
    unsigned char UARTDataBuffer[max_incoming_uart_data_length];
    std::vector<unsigned char> outputBuffer;

    std::shared_ptr<FastUARTIOHandler> uartHandler(new FastUARTIOHandler(UARTport, baud));

    while(true == Gateway::communicationSessionStatus) { std::cout << "UART->WiFi\n";

        /*!
         * Read the UART-available data.
         */
        auto bytesReceived = uartHandler->read(UARTDataBuffer, max_incoming_uart_data_length);


        /*!
         * If there was some data, send it over TCP.
         */
        if(bytesReceived > 0) {
            boost::asio::write((*socket), boost::asio::buffer(UARTDataBuffer, bytesReceived));

            std::cout << "\nSending data to app...\n";
        }
    }

    std::cout << "\nExited: UARTToWifiWorkerSession(...)\n";
}

为了停止这两个线程,我执行以下操作:从WiFiToUARTWorkerSession(...) 函数中,如果read(...) 失败(出现boost::asio::error::eof 之类的错误或任何其他错误)我设置Gateway::communicationSessionStatus 布尔开关(这两个函数共享(全局)到false,这样函数应该返回,线程应该被优雅地终止。

当我第一次连接时,效果很好,但是当我与服务器断开连接时,来自WiFiToUARTWorkerSession(...) 的执行流程通过else if (error) 条件,它设置了while 条件变量到false,然后它抛出boost::system::system_error(error)(这实际上意味着Connection reset by peer)。

然后当我再次尝试连接时,出现以下异常并且程序终止:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
  what():  write: Broken pipe

可能是什么问题?

编辑:从我对这个错误的发现来看,我似乎在客户端断开连接后write(...),但这怎么可能呢?

EDIT2:我已经对代码进行了更多调试,似乎一个线程(在其上运行UARTToWifiWorkerSession(...) 函数)实际上不会退出(因为有一个阻塞的read(...) 函数调用在执行流程停止的地方)。这样一个线程会挂起,直到read(...)函数接收到一些数据,当我重新连接时,会创建另外两个线程,这会导致一些数据竞争问题。

有人可以确认这可能是问题所在吗?

【问题讨论】:

  • 那么,问题出在客户端连接到服务器上,对吗?而且您还没有发布代码的客户端。据我猜测,您正在尝试从同一个套接字建立连接而不先关闭它。
  • @Arunmu 我没有来自客户端的代码,但是您认为客户端会有什么问题?你说我使用的是同一个套接字是什么意思?每个新连接都有一个新套接字。
  • Then when I'm trying to connect again.. 这是在哪里发生的?客户端还是服务器端?
  • @Arunmu 客户端。
  • 所以,当您说There's a new socket for every new connection. 时,您指的是服务器端,而不是客户端。我在这里的猜测是,您的客户端正在尝试使用可能未关闭的旧套接字与服务器建立新连接。

标签: c++ multithreading boost boost-asio


【解决方案1】:

实际的问题是函数UARTToWifiWorkerSession(...) 并没有真正退出(因为阻塞read(...) 函数,这导致两个线程(挂起的一个,以及最近创建的两个线程之一)到write(...) (没有任何并发​​控制)使用相同的套接字。

解决方案是设置read(...) 超时,这样我就可以从函数返回(从而破坏线程)而无需等待某些输入。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多