调用 accept() 导致 WSAEFAULT 10014 Bad address

Calling accept() causes WSAEFAULT 10014 Bad address

我正在为 Windows 编写自定义 TCP 服务器,使用 MinGW 编译器和 winsock2 API。

我有这段代码:

TCPSocket TCPSocket::accept() {

    TCPSocket clSocket;
    struct sockaddr_in clAddr;
    socklen_t clAddrSize;

    clAddrSize = sizeof(clAddr);

    clSocket.shared->sockFd = ::accept(shared->sockFd, (struct sockaddr *)&clAddr, &clAddrSize);
    if (clSocket.shared->sockFd < 0) {
        printf("failed to accept incoming connection (code: %d)\n", WSAGetLastError());
        throw SocketException(6, "failed to accept incoming connection");
    }

    clSocket.shared->buffer = new byte [BUFFER_SIZE];
    clSocket.shared->curPos = clSocket.shared->endPos = clSocket.shared->buffer;

    return clSocket;

}

但是在调用 accept() 之后我得到

failed to accept incoming connection (code: 10014)

根据 MSDN:

WSAEFAULT 10014 Bad address. The system detected an invalid pointer address in attempting to use a pointer argument of a call. This error occurs if an application passes an invalid pointer value, or if the length of the buffer is too small. For instance, if the length of an argument, which is a sockaddr structure, is smaller than the sizeof(sockaddr).

我不明白,这些指针怎么可能是坏的,它们都直接寻址一个局部变量。 clAddrSize被初始化,shared->sockFd在另一个函数

中被初始化
void TCPSocket::listen(uint16_t port, int backlog) {

    struct addrinfo * ainfo;
    char portStr[8];
    int res;

    if (shared->sockFd != -1)
        logicError(1, "socket already initialized, need to close first");

    snprintf(portStr, sizeof(portStr), "%hu", (ushort)port);
    if (getaddrinfo("localhost", portStr, NULL, &ainfo) != 0)
        systemError(2, "failed to retrieve info about localhost", false);

    shared->sockFd = socket(ainfo->ai_family, SOCK_STREAM, IPPROTO_TCP);
    if (shared->sockFd < 0)
        systemError(3, "failed to create a TCP socket", false);

    res = bind(shared->sockFd, ainfo->ai_addr, ainfo->ai_addrlen);
    if (res != 0)
        systemError(5, "failed to bind socket to local port", true);

    res = ::listen(shared->sockFd, backlog);
    if (res != 0)
        systemError(6, "failed to set socket to listen state", true);

    freeaddrinfo(ainfo);

}

你看到我忽略了什么吗?

好的,感谢 CristiFati 我找到了问题所在。 以这种方式使用的函数 getaddrinfo("localhost", portStr, NULL, &ainfo) 返回一个 IPv6 地址。虽然 accept 正在获取 sockaddr_in,这是一个 IPv4 地址的结构。

或许可以通过更多方式解决,例如

  • 使用 sockaddr_in6 进行 IPv6 通信
  • 告诉 getaddrinfo 使用第三个参数只搜索 IPv4 结果
  • 在getaddrinfo返回的链表中取下一个结果

但我选择以这种方式手动初始化 IPv4 协议的套接字:

    struct sockaddr_in myAddr;

    memset(&myAddr, 0, sizeof(myAddr));
    myAddr.sin_family = AF_INET;
    myAddr.sin_port   = htons((ushort)port);

    shared->sockFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (shared->sockFd < 0)
        systemError(3, "failed to create a TCP socket", false);

    res = bind(shared->sockFd, (struct sockaddr *)&myAddr, sizeof(myAddr));
    if (res != 0)
        systemError(5, "failed to bind socket to local port", true);

从那以后,一切正常。