WSL2中TCP服务器跨局域网设备连接超时问题求助
WSL2中TCP服务器无法被局域网客户端连接的排查与解决
问题描述
在Windows机器的WSL2环境中运行TCP服务器,尝试让同局域网内另一台设备的客户端连接时,始终出现连接超时。使用telnet测试连接超时,ping服务器IP无响应。
已尝试的配置与操作
服务器代码(C++)
#include <iostream> #include <cstring> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> using namespace std; struct MessageHeader { uint8_t header_length; uint8_t message_type; uint32_t timestamp; uint16_t sender_id; uint16_t receiver_id; uint32_t message_id; uint16_t payload_length; uint16_t checksum; }; uint16_t calculateChecksum(const uint8_t* data, size_t length) { uint32_t sum = 0; for (size_t i = 0; i < length; ++i) { sum += data[i]; } return static_cast<uint16_t>(~sum); } void handleClient(int client_fd) { MessageHeader header; while (read(client_fd, &header, sizeof(header)) > 0) { switch (header.message_type) { case 1: // ON_REQ cout << "Received online status request" << endl; MessageHeader response; response.header_length = sizeof(MessageHeader); response.message_type = 2; // ON_RES response.timestamp = static_cast<uint32_t>(time(nullptr)); response.sender_id = header.receiver_id; response.receiver_id = header.sender_id; response.message_id = header.message_id; response.payload_length = 0; response.checksum = calculateChecksum(reinterpret_cast<const uint8_t*>(&response), sizeof(response) - sizeof(response.checksum)); send(client_fd, &response, sizeof(response), 0); break; case 3: { // CHAT cout << "Received chat message" << endl; char* payload = new char[header.payload_length]; read(client_fd, payload, header.payload_length); cout << "Message: " << string(payload, header.payload_length) << endl; delete[] payload; // Send ACK MessageHeader ack; ack.header_length = sizeof(MessageHeader); ack.message_type = 4; // ACK ack.timestamp = static_cast<uint32_t>(time(nullptr)); ack.sender_id = header.receiver_id; ack.receiver_id = header.sender_id; ack.message_id = header.message_id; ack.payload_length = 0; ack.checksum = calculateChecksum(reinterpret_cast<const uint8_t*>(&ack), sizeof(ack) - sizeof(ack.checksum)); send(client_fd, &ack, sizeof(ack), 0); break; } default: cerr << "Unknown message type received" << endl; break; } } } int main() { int server_fd, client_fd; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { cerr << "Socket creation error" << endl; return -1; } if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { cerr << "Setsockopt error" << endl; return -1; } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; // Bind to all network interfaces address.sin_port = htons(8080); if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { cerr << "Bind failed" << endl; return -1; } if (listen(server_fd, 3) < 0) { cerr << "Listen failed" << endl; return -1; } cout << "Server listening on port 8080" << endl; if ((client_fd = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { cerr << "Accept failed" << endl; return -1; } handleClient(client_fd); close(client_fd); close(server_fd); return 0; }
客户端代码(C++)
#include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <ctime> #include <thread> #include <atomic> #include <cerrno> using namespace std; struct MessageHeader { uint8_t header_length; uint8_t message_type; uint32_t timestamp; uint16_t sender_id; uint16_t receiver_id; uint32_t message_id; uint16_t payload_length; uint16_t checksum; }; uint16_t calculateChecksum(const uint8_t* data, size_t length) { uint32_t sum = 0; for (size_t i = 0; i < length; ++i) { sum += data[i]; } return static_cast<uint16_t>(~sum); } void sendOnlineStatusRequest(int sock) { MessageHeader header; header.header_length = sizeof(MessageHeader); header.message_type = 1; // ON_REQ header.timestamp = static_cast<uint32_t>(time(nullptr)); header.sender_id = 1; // Example sender ID header.receiver_id = 2; // Example receiver ID header.message_id = 12345; // Example message ID header.payload_length = 0; header.checksum = calculateChecksum(reinterpret_cast<const uint8_t*>(&header), sizeof(header) - sizeof(header.checksum)); send(sock, &header, sizeof(header), 0); } void sendChatMessage(int sock, const string& message) { MessageHeader header; header.header_length = sizeof(MessageHeader); header.message_type = 3; // CHAT header.timestamp = static_cast<uint32_t>(time(nullptr)); header.sender_id = 1; // Example sender ID header.receiver_id = 2; // Example receiver ID header.message_id = 12346; // Example message ID header.payload_length = message.size(); header.checksum = 0; // Initial checksum size_t total_size = sizeof(header) + message.size(); uint8_t* buffer = new uint8_t[total_size]; memcpy(buffer, &header, sizeof(header)); memcpy(buffer + sizeof(header), message.c_str(), message.size()); header.checksum = calculateChecksum(buffer, total_size); memcpy(buffer, &header, sizeof(header)); send(sock, buffer, total_size, 0); delete[] buffer; } void handleServerResponse(int sock, atomic<bool>& running) { MessageHeader response; while (running) { int bytesRead = read(sock, &response, sizeof(response)); if (bytesRead > 0) { switch (response.message_type) { case 2: // ON_RES cout << "Received online status response" << endl; break; case 4: // ACK cout << "Received ACK for message ID: " << response.message_id << endl; break; case 5: // NACK cerr << "Received NACK for message ID: " << response.message_id << endl; break; case 6: // ERR cout << "Received error message" << endl; break; default: cerr << "Unknown message type received" << endl; break; } } } } int main() { int sock = 0; struct sockaddr_in serv_addr; atomic<bool> running(true); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { cerr << "Socket creation error" << endl; return -1; } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(8080); // Replace with the Windows host IP address obtained from `ipconfig` serv_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // Example IP, replace with actual IP if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { cerr << "Connection failed: " << strerror(errno) << endl; // Print the error message return -1; } sendOnlineStatusRequest(sock); thread responseThread(handleServerResponse, sock, ref(running)); string message; while (running) { cout << "Enter message: "; getline(cin, message); if (message == "quit") { running = false; break; } sendChatMessage(sock, message); } running = false; responseThread.join(); close(sock); return 0; }
已做配置
- 客户端与服务器设备处于同一局域网
- 通过
ipconfig确认了Windows主机的IP地址 - 尝试配置WSL2端口转发
可能的原因及解决办法
1. 错误使用了WSL2内部IP而非Windows主机IP
从你的telnet和ping命令看,你连接的是172.31.x.x段的IP,这是WSL2的内部虚拟IP,局域网其他设备无法直接访问。客户端必须连接Windows主机的局域网IP(通常是192.168.x.x或10.x.x.x段)。
解决:
- 在Windows主机上运行
ipconfig,找到当前联网适配器(如Wi-Fi或以太网)的IPv4地址,比如192.168.1.100 - 修改客户端代码中的服务器IP为该地址,重新编译运行
2. WSL2端口转发未正确配置或失效
WSL2的网络是NAT模式,默认无法直接被外部访问,需要手动配置端口转发,且重启WSL或Windows后转发规则会失效。
检查并配置转发:
- 在WSL2中运行
ip addr,找到WSL的内部IP(通常是eth0下的inet地址,如172.31.136.105) - 在Windows管理员命令提示符中执行:
替换netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8080 connectaddress=WSL内部IP connectport=8080WSL内部IP为实际地址 - 验证转发规则:
netsh interface portproxy show v4tov4
3. Windows防火墙或第三方防火墙拦截
Windows防火墙默认会阻止外部设备访问主机端口,需要手动添加入站规则。
解决:
- 打开Windows Defender防火墙 -> 高级设置 -> 入站规则 -> 新建规则
- 选择
端口-> 下一步 -> 选择TCP,输入8080-> 下一步 -> 允许连接 -> 下一步 -> 勾选所有网络类型 -> 命名规则(如"WSL2 TCP 8080")
4. WSL2服务器绑定问题
你的服务器代码中已经设置INADDR_ANY,这是正确的,会绑定到WSL的所有网络接口,无需修改。但可以验证服务器是否真的在监听:
在WSL2中执行:
ss -tulnp | grep 8080
如果输出包含LISTEN且对应你的进程,说明监听正常。
5. 客户端设备网络问题
- 确认客户端设备和Windows主机在同一局域网(比如连接同一Wi-Fi或交换机)
- 尝试从客户端ping Windows主机的局域网IP,如果不通,排查客户端或路由器的网络配置
验证步骤
- 在Windows主机上用
telnet localhost 8080测试,确认服务器能被本地访问 - 同一局域网内另一台Windows设备用
telnet Windows主机IP 8080测试,确认转发和防火墙配置正确 - 最后运行客户端代码,连接Windows主机IP的8080端口
内容的提问来源于stack exchange,提问作者Carlos Valeriano
相关产品推荐
相关产品推荐

