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

Winsock2套接字本地通信正常,远程连接失败求助

TCP远程连接失败(错误码10057)排查与修复

问题背景

基于Winsock实现的TCP客户端/服务端,本地运行时聊天功能正常,但局域网或远程连接时通信失败。客户端调用recv时触发错误,WSAGetLastError返回10057(套接字未连接时尝试发送/接收数据)。已关闭防火墙并配置端口转发,程序使用非阻塞套接字。

服务端代码

#include "sockets.h"

int main(){

    // SETUP AND BIND SERVER SOCKET

    SOCKET server_socket = socket_setup();
    SOCKADDR_IN address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("192.168.86.179"); //using LAN IP, not local (127.0.0.1)
    address.sin_port = htons(6502);

    if(bind(server_socket, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR)
        WSA_ERR("Socket Bind Error");

    if(listen(server_socket, 5) == SOCKET_ERROR)
        WSA_ERR("Listening Error");

    SOCKET accept_socket = SOCKET_ERROR;
    do{
        accept_socket = accept(server_socket, NULL, NULL);
    }
    while(accept_socket == SOCKET_ERROR);

    // SEND AND RECEIVE BYTES

    char sendmsg[100] = "Hello Client";
    char recvmsg[100] = "";

    int bytes_sent = send(accept_socket, sendmsg, strlen(sendmsg), 0);
    if(bytes_sent == SOCKET_ERROR){
        WSA_ERR("Send Error");
    }else{
        printf("Send OK\n");
        printf("Bytes Sent: %ld\n", bytes_sent);
        printf("%s\n", sendmsg);
    }

    int bytes_rcvd = blocking_recv(accept_socket, recvmsg);
    if(bytes_rcvd == SOCKET_ERROR){
        WSA_ERR("Recv Error");
    }else{
        printf("Recv OK\n");
        printf("Bytes Rcvd: %ld\n", bytes_rcvd);
        printf("%s\n", recvmsg);
    }

    chat_loop(recvmsg, sendmsg, accept_socket);
    return 0;
}

客户端代码

#include "sockets.h"

int main(){

    // SETUP AND CONNECT CLIENT SOCKET

    SOCKET client_socket = socket_setup();
    SOCKADDR_IN address;
    address.sin_family = AF_INET;
    printf("Enter IP address:\n");
    char ip[15];
    gets_s(ip, 15);
    address.sin_addr.s_addr = inet_addr(ip);
    address.sin_port = htons(6502);

    if(connect(client_socket, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK){
        WSA_ERR("Connection Error");
    }

    // SEND AND RECIEVE BYTES

    char sendmsg[100] = "Hello Server";
    char recvmsg[100] = "";

    int bytes_rcvd = blocking_recv(client_socket, recvmsg);
    if(bytes_rcvd == SOCKET_ERROR){
        WSA_ERR("Recv Error"); //<-- WSAGetLastError() from inside macro gives error 10057 here
    }else{
        printf("Recv OK\n");
        printf("Bytes Rcvd: %ld\n", bytes_rcvd);
        printf("%s\n", recvmsg);
    }

    int bytes_sent = send(client_socket, sendmsg, strlen(sendmsg), 0);
    if(bytes_sent == SOCKET_ERROR){
        WSA_ERR("Send Error");
    }else{
        printf("Send OK\n");
        printf("Bytes Sent: %ld\n", bytes_sent);
        printf("%s\n", sendmsg);
    }

    chat_loop(recvmsg, sendmsg, client_socket);

    system("pause");
    return 0;
}

sockets.h 核心函数

int blocking_recv(SOCKET sock, char buf[]){
    int result = 0;
    do{
        result = recv(sock, buf, 100, 0);
    }while(result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK);
    return result;
}

核心问题分析

错误码10057的直接原因是:客户端在套接字未完成连接建立的情况下,就调用了recv操作。

客户端使用非阻塞套接字,但对connect的处理存在逻辑漏洞:

  • 非阻塞模式下,connect会立即返回SOCKET_ERROR,同时WSAGetLastError返回WSAEWOULDBLOCK,这表示连接正在异步建立中,而非连接失败。
  • 当前代码仅判断了WSAEWOULDBLOCK就跳过错误处理,但没有等待连接真正建立完成,直接进入recv流程,此时套接字实际上还未处于已连接状态,导致10057错误。

另外服务端绑定存在优化空间:

  • 服务端绑定了固定LAN IP192.168.86.179,如果服务器有多网卡或IP变动,会导致监听失效,建议改为绑定INADDR_ANY(允许接收来自所有网卡的连接)。

修复方案

1. 修复客户端非阻塞connect的连接等待逻辑

非阻塞模式下,需要通过select等待连接完成,确保套接字进入已连接状态后再进行收发操作:

// 客户端connect后的等待逻辑,放在connect之后、recv之前
fd_set write_set;
FD_ZERO(&write_set);
FD_SET(client_socket, &write_set);

struct timeval timeout;
timeout.tv_sec = 10; // 设置10秒超时
timeout.tv_usec = 0;

// 等待套接字可写(表示连接完成)
int ret = select(0, NULL, &write_set, NULL, &timeout);
if (ret == SOCKET_ERROR) {
    WSA_ERR("select error");
} else if (ret == 0) {
    printf("Connect timeout\n");
    closesocket(client_socket);
    return 1;
} else {
    // 检查连接是否真的成功建立
    int error = 0;
    int len = sizeof(error);
    getsockopt(client_socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
    if (error != 0) {
        WSA_ERR("Connect failed after select");
    }
}

2. 优化服务端绑定地址

将服务端的绑定IP改为INADDR_ANY,适配多网卡场景:

// 替换原有的inet_addr("192.168.86.179")
address.sin_addr.s_addr = htonl(INADDR_ANY);

3. 补充非阻塞套接字的错误处理细节

  • blocking_recv函数中,循环处理WSAEWOULDBLOCK是正确的,但要注意如果返回0表示对方关闭连接,需要在调用处处理这种情况。
  • 客户端gets_s读取IP时,要校验输入合法性(比如是否是有效IP),避免inet_addr返回INADDR_NONE导致连接失败。

内容的提问来源于stack exchange,提问作者Joshua White

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:24:58