连接到 IPv4 地址时 C++ Winsock 连接被拒绝

问题描述 投票:0回答:3

我正在使用winsock2 对服务器/客户端系统进行编程,当我将客户端连接到服务器名称服务器IPv6 地址时,它工作得很好。但是,当我使用 服务器 IPv4 地址 时,我在客户端中调用 connect() 时收到错误“连接被拒绝”。

我的客户端或使用 telnet 时会发生此错误。但是,我可以使用 IPv4 或 IPv6 这三个名称之一成功 ping 服务器。

我尝试过在同一台计算机上、不同的计算机上运行服务器和客户端,并且在所有计算机上禁用防火墙。

这是我的服务器初始化和监听代码的摘录:

SOCKET sockfd = INVALID_SOCKET, in_socketID;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
sockaddr_storage incoming_addr;
int addr_size;
int tmp_err;
const char *sPort = "20152";

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

tmp_err = getaddrinfo(NULL, sPort, &hints, &servinfo);
if (tmp_err != 0)
    throw exception("ERROR: getaddrinfo failed");

// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
    ip = p;
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == INVALID_SOCKET)
    {
        cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
    } // end if
    else if (bind(sockfd, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
    {
        cerr << "ERROR on bind(): " << WSAGetLastError() << endl;
        closesocket(sockfd);
        sockfd = INVALID_SOCKET;
    } // end if
} // end for

if (sockfd == INVALID_SOCKET)
{
    // looped off the end of the list with no successful bind
    throw exception("ERROR: Failed to bind socket");
}

// clean up
if (servinfo)
    freeaddrinfo(servinfo);

if (listen(sockfd, SOMAXCONN ) == SOCKET_ERROR)
    throw exception("Listen failed");

while (true)
{
    memset(&incoming_addr, 0, sizeof(incoming_addr));
    addr_size = sizeof(incoming_addr);
    in_socketID = accept(socketID, (sockaddr *)&incoming_addr, &addr_size);

    // do stuff with incoming connection
}

这是我的客户代码:

int sockfd = INVALID_SOCKET;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
int tmp_err;
const char *sHost = "192.168.1.136";
const char *sPort = "20152";

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // use TCP

tmp_err = getaddrinfo(sHost,        // web address or ip to connect to
                      sPort,        // port or protocol
                      &hints,       // initialized hints structure
                      &servinfo);   // return structure
if (tmp_err != 0)
    throw exception("ERROR: getaddrinfo failed");

// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
    ip = p;
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == INVALID_SOCKET)
    {
        cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
        //continue;
    } // end if
    else if (connect(sockfd, p->ai_addr, p->ai_addrlen) < 0)
    {
        cerr << "ERROR on connect(): " << WSAGetLastError() << endl;
        closesocket(sockfd);
        sockfd = INVALID_SOCKET;
        //continue;
    } // end if
} // end for

if (sockfd == INVALID_SOCKET)
    throw exception("ERROR: Failed to connect");


// clean up
if (servinfo)
    freeaddrinfo(servinfo);

// do stuff with new socket

我已经在网站上阅读了几个类似的问题,但没有人回答这个问题。

如何连接到服务器 IPv4 地址?我需要帮助,请。

我能够通过在服务器上进行更改来连接到 IPv4

hints.ai_family = AF_UNSPEC;

hints.ai_family = AF_INET;

我知道它显然会起作用,但是当我这样做时,IPv6 当然不起作用。

看来用户 Sorayuki 是对的,我的环路正在连接到 IPv6。

似乎没有简单的方法来统一 IPv6 和 IPv4。您的套接字必须侦听其中之一,这使得该过程非常烦人。

根据文档,监听 IPv4 和 IPv6 的旧方式是为每个创建一个套接字并监听两者。这适用于 Windows Server 2003 和 Windows XP SP1。

首选的现代风格(Windows Vista、7 和 8)是将您的套接字变成双套接字,它将同时侦听 IPv4 和 IPv6。但是,您的客户端还必须能够设置双套接字,因此,如果您的应用程序为旧客户端提供服务,您将不得不使用旧方法。

c++ ipv6 winsock ipv4
3个回答
3
投票

这是因为绑定到 IPv6 地址并不会神奇地绑定到 IPv4 地址。

在 Linux 上,默认情况下绑定到

[::]
将导致 IPv6 和 IPv4 正常工作(除非
/proc/sys/net/ipv6/bindv6only
设置为
1
)。

但是,在 Mac OS X 和 Windows 上,绑定到

[::]
仅适用于 IPv6。您还必须绑定到 IPv4 地址(或
0.0.0.0
)才能正常工作。

您的评论中描述的逻辑“循环遍历所有结果并绑定到我们可以的第一个结果”正是这里的问题。您应该使用

[::]
标志(参见
IPV6_V6ONLY
)和
setsockopt()
绑定到
0.0.0.0


2
投票

是否因为您将服务器套接字绑定到了 IPv6 地址? 在“for”循环中,IPv6 地址出现在 IPv4 地址之前似乎会导致服务器的套接字侦听 IPv6 地址。 所以你的服务器没有监听任何 IPv4 地址,导致所有与服务器 IPv4 地址的连接都被拒绝。

尝试使用工具或某些命令(例如netstat)查看所有侦听端口位于哪个IP地址上


-1
投票

您是否尝试过在同一台机器上运行服务器和客户端?

这听起来像是防火墙问题。 如果您在同一台计算机上成功连接 telnet/您的应用程序,您就会知道这就是问题所在。

© www.soinside.com 2019 - 2024. All rights reserved.