基于C++/boost socket的简单客户端/服务端Windows正常Linux失效
我实现了一个极简的boost::socket客户端/服务端,需求是服务端持续运行,支持客户端反复连接、发送数据、断开。Windows下运行完全正常:服务端启动后输出对应信息,客户端连接后服务端能正常接收数据、响应断开操作;按下服务端控制台Enter键可干净退出。但在Linux下编译运行同一代码时,客户端输出正常,服务端却完全无响应,请问这是什么原因?
服务端代码
#include <iostream> #include <boost/asio.hpp> #include <boost/thread.hpp> using boost::asio::ip::tcp; class TheServer { public: TheServer(int port) : m_port(port) { m_pIOService = new boost::asio::io_service; m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this)); listenForNewConnection(); } ~TheServer() { m_bContinueReading = false; m_pIOService->stop(); m_pThread->join(); delete m_pThread; delete m_pSocket; delete m_pAcceptor; delete m_pIOService; } void listenForNewConnection() { if (m_pSocket) delete m_pSocket; if (m_pAcceptor) delete m_pAcceptor; // start new acceptor operation m_pSocket = new tcp::socket(*m_pIOService); m_pAcceptor = new tcp::acceptor(*m_pIOService, tcp::endpoint(tcp::v4(), m_port)); std::cout << "Starting async_accept" << std::endl; m_pAcceptor->async_accept(*m_pSocket, boost::bind<void>(&TheServer::readSession, this, boost::asio::placeholders::error)); } void readSession(boost::system::error_code error) { if (!error) { std::cout << "Connection established" << std::endl; while ( m_bContinueReading ) { static unsigned char buffer[1000]; boost::system::error_code error; size_t length = m_pSocket->read_some(boost::asio::buffer(&buffer, 1000), error); if (!error && length != 0) { std::cout << "Received " << buffer << std::endl; } else { std::cout << "Received error, connection likely closed by peer" << std::endl; break; } } std::cout << "Connection closed" << std::endl; listenForNewConnection(); } else { std::cout << "Connection error" << std::endl; } std::cout << "Ending readSession" << std::endl; } void run() { while (m_bContinueReading) m_pIOService->run_one(); std::cout << "Exiting run thread" << std::endl; } bool m_bContinueReading = true; boost::asio::io_service* m_pIOService = NULL; tcp::socket* m_pSocket = NULL; tcp::acceptor* m_pAcceptor = NULL; boost::thread* m_pThread = NULL; int m_port; }; int main(int argc, char* argv[]) { TheServer* server = new TheServer(1900); std::cout << "Press Enter to quit" << std::endl; std::string sGot; getline(std::cin, sGot); delete server; return 0; }
客户端代码
#include <iostream> #include <boost/asio.hpp> #include <boost/thread.hpp> int main(int argc, char* argv[]) { std::cout << std::endl; std::cout << "Starting client" << std::endl; using boost::asio::ip::tcp; boost::asio::io_service* m_pIOService = NULL; tcp::socket* m_pSocket = NULL; try { m_pIOService = new boost::asio::io_service; std::stringstream sPort; sPort << 1900; tcp::resolver resolver(*m_pIOService); tcp::resolver::query query(tcp::v4(), "localhost", sPort.str()); tcp::resolver::iterator iterator = resolver.resolve(query); m_pSocket = new tcp::socket(*m_pIOService); m_pSocket->connect(*iterator); std::cout << "Client conected" << std::endl; std::string hello = "Hello World"; boost::asio::write( *m_pSocket, boost::asio::buffer(hello.data(), hello.size()) ); boost::this_thread::sleep(boost::posix_time::milliseconds(100)); hello += "(2)"; boost::asio::write(*m_pSocket, boost::asio::buffer(hello.data(), hello.size())); } catch (std::exception& e) { delete m_pSocket; m_pSocket = NULL; std::cerr << "Exception: " << e.what() << std::endl; } return 0; }
问题原因分析
TCP端口重用限制:
Linux系统下,当服务端关闭旧的acceptor和socket后,对应端口会进入TIME_WAIT状态,默认不允许立即重新绑定同一端口。而服务端在listenForNewConnection中创建新tcp::acceptor时,未设置reuse_address选项,导致绑定操作失败,async_accept无法正常启动,因此服务端无法响应客户端的连接请求。Windows系统对端口重用的限制更宽松,所以不会触发该问题。多线程内存可见性问题:
成员变量m_bContinueReading是普通bool类型,在多线程环境下(主线程修改、IO线程读取)存在内存可见性问题,Linux下的内存模型可能导致IO线程无法及时感知主线程的修改,进一步加剧无响应的表象。标准输出缓冲差异:
Linux下stdout默认是行缓冲模式,非主线程的输出可能因缓冲未及时刷新,导致信息无法立即显示在控制台,让服务端看起来完全无响应。
修复方案
添加端口重用选项:
在创建tcp::acceptor后,设置reuse_address允许端口重用:m_pAcceptor = new tcp::acceptor(*m_pIOService, tcp::endpoint(tcp::v4(), m_port)); m_pAcceptor->set_option(boost::asio::socket_base::reuse_address(true)); // 新增此行优化线程同步:
将m_bContinueReading改为原子类型,确保多线程下的内存可见性:#include <atomic> std::atomic<bool> m_bContinueReading = true;改进内存管理:
使用智能指针(如std::unique_ptr)替代裸指针,避免内存泄漏和悬空指针问题。
内容的提问来源于stack exchange,提问作者jpo38

