boost :: asio写道:管道破裂

时间:2016-06-24 07:21:11

标签: c++ multithreading boost boost-asio

我有一个处理新连接的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(...)函数)实际上不会退出(因为那里&#39;在执行流程停止的地方阻塞read(...)函数调用。这样一个线程将挂起,直到read(...)函数接收到一些数据,并且当我重新连接时,将创建另外两个线程,这会导致一些数据竞争问题。

有人可以证实我这可能是问题吗?

1 个答案:

答案 0 :(得分:0)

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

解决方案是设置read(...)超时,这样我就可以从函数返回(从而破坏线程),而不会从某些输入中挂起。