首页 文章

连接到IPv4地址时,C winsock连接被拒绝

提问于
浏览
3

我正在使用winsock2编写服务器/客户端系统,当我将客户端连接到 server nameserver IPv6 address 时它很有用 . 但是,当我使用 server IPv4 address 时,我从客户端的connect()调用中得到错误"Connection refused" .

我的客户端或使用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地址?我需要帮助 .

谢谢 .

编辑:

根据用户Sorayuki给出的建议,我做了一些修改,只是为了测试他的理论是否正确 .

通过更改服务器,我能够连接到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 . 但是,您的客户端还必须能够设置双插槽,因此,如果您的应用程序正在为旧客户端提供服务,那么您将无法使用旧方法 .

谢谢!

3 回答

  • 3

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

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

  • -1

    这是因为绑定到IPv6地址也不会神奇地绑定到IPv4地址 .

    在Linux上,默认情况下绑定到 [::] 将导致IPv6和IPv4工作(除非 /proc/sys/net/ipv6/bindv6only 设置为 1 ) .

    但是,在Mac OS X和Windows上,绑定到 [::] 仅适用于IPv6 . 您还必须绑定到IPv4地址(或 0.0.0.0 )才能使其正常工作 .

    您的评论"loop through all the results and bind to the first we can"中描述的逻辑正是这里的问题 . 您应该使用 IPV6_V6ONLY 标志(请参阅 setsockopt() )和 0.0.0.0 绑定到 [::] .

  • 1

    你试过在同一台机器上运行服务器和客户端吗?

    这听起来像防火墙问题 . 如果你成功连接telnet /你的应用程序在同一台机器上你会知道这是问题所在 .

相关问题