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 从函数中退出(从而销毁线程)而无需等待某些输入。
我有一个处理新连接的 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 从函数中退出(从而销毁线程)而无需等待某些输入。