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

如何修改Winsock客户端服务器代码以支持localhost/127.0.0.1连接

问题

已下载并编译微软Winsock客户端与服务器代码示例,同一计算机上运行时,客户端传入主机名或本地IPv4地址可正常连接,但传入"localhost"或"127.0.0.1"无法连接,需修改代码使这两种地址也能正常连接。

原因分析

问题核心在于服务器的地址监听配置:

  • 原服务器代码将hints.ai_family设为AF_INET,仅支持IPv4地址解析,且若系统中localhost优先解析为IPv6地址(::1),客户端会尝试连接未被监听的IPv6端口,导致失败。
  • 若服务器实际绑定的是特定网卡的IPv4地址而非通配地址0.0.0.0,回环地址127.0.0.1也无法匹配服务器监听端口。
修改方案

方案一:修改服务器,支持双栈监听(推荐)

让服务器同时监听IPv4和IPv6的所有本地地址,覆盖回环地址:

  1. 将hints.ai_family从AF_INET改为AF_UNSPEC,允许解析IPv4和IPv6地址。
  2. 新增循环逻辑,为getaddrinfo返回的每个地址创建并绑定监听套接字,确保所有可用地址都被监听。
  3. 对IPv6套接字禁用IPV6_V6ONLY选项,使其可同时接受IPv4连接。

方案二:修改客户端,仅解析IPv4地址

如果只需支持IPv4连接,限制客户端仅解析IPv4地址:
将客户端代码中的hints.ai_family从AF_UNSPEC改为AF_INET,这样localhost会直接解析为127.0.0.1,匹配服务器的IPv4监听端口。

修改后的代码

修改后的服务器代码(方案一)

#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

// 创建并配置单个监听套接字
SOCKET setup_listen_socket(struct addrinfo *addr) {
    SOCKET ListenSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        return INVALID_SOCKET;
    }

    // 禁用IPv6套接字的V6ONLY选项,允许接受IPv4连接
    if (addr->ai_family == AF_INET6) {
        DWORD v6only = 0;
        if (setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6only, sizeof(v6only)) == SOCKET_ERROR) {
            printf("setsockopt IPV6_V6ONLY failed with error: %d\n", WSAGetLastError());
            closesocket(ListenSocket);
            return INVALID_SOCKET;
        }
    }

    if (bind(ListenSocket, addr->ai_addr, (int)addr->ai_addrlen) == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        return INVALID_SOCKET;
    }

    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        return INVALID_SOCKET;
    }

    printf("Listening on %s\n", addr->ai_family == AF_INET ? "IPv4" : "IPv6");
    return ListenSocket;
}

int __cdecl main(void) 
{
    WSADATA wsaData;
    int iResult;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;  // 修改为AF_UNSPEC,支持IPv4和IPv6
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if ( iResult != 0 ) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建多个监听套接字,覆盖所有解析到的地址
    SOCKET* listen_sockets = malloc(sizeof(SOCKET) * 10);
    int socket_count = 0;
    struct addrinfo *ptr = result;
    while (ptr != NULL) {
        SOCKET sock = setup_listen_socket(ptr);
        if (sock != INVALID_SOCKET) {
            listen_sockets[socket_count++] = sock;
        }
        ptr = ptr->ai_next;
    }

    freeaddrinfo(result);

    if (socket_count == 0) {
        printf("Failed to create any listening socket!\n");
        WSACleanup();
        return 1;
    }

    // 等待任意监听套接字的连接请求
    fd_set read_fds;
    SOCKET ClientSocket = INVALID_SOCKET;
    while (ClientSocket == INVALID_SOCKET) {
        FD_ZERO(&read_fds);
        for (int i = 0; i < socket_count; i++) {
            FD_SET(listen_sockets[i], &read_fds);
        }

        iResult = select(0, &read_fds, NULL, NULL, NULL);
        if (iResult == SOCKET_ERROR) {
            printf("select failed with error: %d\n", WSAGetLastError());
            for (int i = 0; i < socket_count; i++) {
                closesocket(listen_sockets[i]);
            }
            free(listen_sockets);
            WSACleanup();
            return 1;
        }

        // 处理有连接请求的套接字
        for (int i = 0; i < socket_count; i++) {
            if (FD_ISSET(listen_sockets[i], &read_fds)) {
                ClientSocket = accept(listen_sockets[i], NULL, NULL);
                if (ClientSocket == INVALID_SOCKET) {
                    printf("accept failed with error: %d\n", WSAGetLastError());
                } else {
                    printf("Accepted connection from a client\n");
                }
                break;
            }
        }
    }

    // 关闭所有监听套接字
    for (int i = 0; i < socket_count; i++) {
        closesocket(listen_sockets[i]);
    }
    free(listen_sockets);

    // 接收并回显客户端数据
    do {
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else  {
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }

    } while (iResult > 0);

    // 关闭发送方向
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    // 清理资源
    closesocket(ClientSocket);
    WSACleanup();

    return 0;
}

修改后的客户端代码(方案二)

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

int __cdecl main(int argc, char **argv) 
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
                       *ptr = NULL,
                       hints;
    const char *sendbuf = "this is a test";
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;

    // 校验参数
    if (argc != 2) {
        printf("usage: %s server-name\n", argv[0]);
        return 1;
    }

    // 初始化Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_INET;  // 修改为AF_INET,仅解析IPv4地址
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // 解析服务器地址和端口
    iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
    if ( iResult != 0 ) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 尝试连接到解析到的地址,直到成功
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {

        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }

        iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    // 发送测试数据
    iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    printf("Bytes Sent: %ld\n", iResult);

    // 关闭发送方向
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // 接收服务器回显数据,直到连接关闭
    do {

        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if ( iResult > 0 )
            printf("Bytes received: %d\n", iResult);
        else if ( iResult == 0 )
            printf("Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());

    } while( iResult > 0 );

    // 清理资源
    closesocket(ConnectSocket);
    WSACleanup();

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:57:03