【问题标题】:Calling accept() causes WSAEFAULT 10014 Bad address调用 accept() 导致 WSAEFAULT 10014 Bad address
【发布时间】:2015-10-17 23:37:02
【问题描述】:

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

我有这段代码:

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 地址不好。 系统在尝试使用调用的指针参数时检测到无效的指针地址。如果应用程序发生此错误 传递了一个无效的指针值,或者缓冲区的长度太长 小的。例如,如果参数的长度是 sockaddr 结构,小于 sizeof(sockaddr)。

我不明白,这些指针是怎么坏的,它们都直接寻址一个局部变量。 clAddrSize 被初始化,shared-&gt;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);

}

你看到我忽略了什么吗?

【问题讨论】:

  • 闻起来像损坏的内存管理,在调用accept()之前发生(方式)。
  • socklen_taccepts() 的最后一个参数(由您使用的 API 定义)大小是否匹配?
  • typedef int socklen_t; WINSOCK_API_LINKAGE SOCKET PASCAL 接受(SOCKET,struct sockaddr*,int*);所以是的,它是一样的
  • 你也有机会在 IPv6 上收听吗? accept 调用返回一个 IPv6 套接字 (struct sockaddr_in6),它太大而无法放入 struct sockaddr_in 变量?为了便于携带,应该使用struct sockaddr_storage
  • @Youda008:那肯定会落入@987654333的“...缓冲区的长度太小 ...”子句中@。如果您不准备正确处理 IPv6,另一种方法是通过 hints 参数将 getaddrinfo() 限制为 IPv4。

标签: c++ c networking tcp winsock2


【解决方案1】:

好的,感谢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);

从那以后,一切正常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-29
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 2015-12-16
    相关资源
    最近更新 更多