boost::asio 写:破管

boost::asio write: Broken pipe

我有一个处理新连接的 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,这样函数应该 return,线程应该被优雅地杀死。

当我第一次连接时,这很好用,但是当我与服务器断开连接时,来自 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

可能是什么问题?

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

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

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

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

解决方案是设置一个 read(...) 超时,这样我就可以 return 从函数中退出(从而销毁线程)而无需等待某些输入。