You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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后转发规则会失效。

检查并配置转发:

  1. 在WSL2中运行ip addr,找到WSL的内部IP(通常是eth0下的inet地址,如172.31.136.105)
  2. 在Windows管理员命令提示符中执行:
    netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8080 connectaddress=WSL内部IP connectport=8080
    
    替换WSL内部IP为实际地址
  3. 验证转发规则:
    netsh interface portproxy show v4tov4
    

3. Windows防火墙或第三方防火墙拦截

Windows防火墙默认会阻止外部设备访问主机端口,需要手动添加入站规则。

解决:

  1. 打开Windows Defender防火墙 -> 高级设置 -> 入站规则 -> 新建规则
  2. 选择端口 -> 下一步 -> 选择TCP,输入8080 -> 下一步 -> 允许连接 -> 下一步 -> 勾选所有网络类型 -> 命名规则(如"WSL2 TCP 8080")

4. WSL2服务器绑定问题

你的服务器代码中已经设置INADDR_ANY,这是正确的,会绑定到WSL的所有网络接口,无需修改。但可以验证服务器是否真的在监听:
在WSL2中执行:

ss -tulnp | grep 8080

如果输出包含LISTEN且对应你的进程,说明监听正常。

5. 客户端设备网络问题

  • 确认客户端设备和Windows主机在同一局域网(比如连接同一Wi-Fi或交换机)
  • 尝试从客户端ping Windows主机的局域网IP,如果不通,排查客户端或路由器的网络配置

验证步骤

  1. 在Windows主机上用telnet localhost 8080测试,确认服务器能被本地访问
  2. 同一局域网内另一台Windows设备用telnet Windows主机IP 8080测试,确认转发和防火墙配置正确
  3. 最后运行客户端代码,连接Windows主机IP的8080端口

内容的提问来源于stack exchange,提问作者Carlos Valeriano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 16:04:59