我有一个处理新连接的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(...)
函数接收到一些数据,并且当我重新连接时,将创建另外两个线程,这会导致一些数据竞争问题。
有人可以证实我这可能是问题吗?
答案 0 :(得分:0)
实际问题是函数UARTToWifiWorkerSession(...)
实际上没有退出(因为阻塞read(...)
函数,这导致两个线程(挂起的一个,最近两个创建的一个)到write(...)
(没有任何并发控制)使用相同的套接字。
解决方案是设置read(...)
超时,这样我就可以从函数返回(从而破坏线程),而不会从某些输入中挂起。