2016-04-12 32 views
0

我用下面的代码来套接字连接到服务器上的Ubuntu 15.10运行:插座连接到一个端口上不存在的IP地址

void Connect(std::string address, int port) 
    { 
      struct addrinfo hints; 
      struct addrinfo *result = NULL; 
      struct addrinfo *rp = NULL; 
      int sfd, s; 

      std::cout << "Connecting to address " << address << " port " << port << std::endl; 

      std::memset(&hints, 0, sizeof(struct addrinfo)); 

      hints.ai_family = AF_UNSPEC;  /* Allow IPV4 or IPV6 */ 
      hints.ai_socktype = SOCK_DGRAM;  /* Datagram socket */ 
      hints.ai_flags = 0; 
      hints.ai_protocol = 0;    /* Any protocol */ 

      std::string portStr; 
      portStr = std::to_string(port); 

      s = getaddrinfo(address.c_str(), portStr.c_str(), &hints, &result); 

std::cout << "ADDRESS-------------> " << s << std::endl; 
      if (s != 0) 
      { 
       std::stringstream ss; 
       ss << "Cannot resolve hostname " << address << gai_strerror(s); 
       throw std::runtime_error(ss.str()); 
      } 

      /* 
      * getaddrinfo() returns a list of address structures. We should try each 
      * address until we successfull bind. If socket() or connect() fails, we close the socket 
      * and try the next address until the end. 
      */ 
      for (rp = result; rp != NULL; rp = rp->ai_next) 
      { 
    std::cout << "loop-----------------> " << rp->ai_family << std::endl; 
       sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 
    std::cout << "sfd-----------------> " << sfd << std::endl; 

       if (sfd == -1) 
        continue; 

    /* 
       * If connect succeed, the address was found. 
       */ 
       int sts = connect(sfd, rp->ai_addr, rp->ai_addrlen); 

    std::cout << "sts-----------------> " << sts << std::endl; 

       if (sts == 0) 
        break; 

       close(sfd); 
      } 

      /* 
      * Check for failure 
      */ 
      if (rp == NULL) 
      { 
       std::stringstream ss; 
       ss << "Cannot find server address at " << address << " port " << port; 
       throw std::runtime_error(ss.str()); 
      } 

      freeaddrinfo(result); /* Object no longer needed */ 

    std::cout << "SOCKET-----------------> " << sfd << std::endl; 

      currentSocket = sfd; 

    } 

我的问题是,这个代码是连接插座,甚至如果IP地址不可用。检查结果:

下面是运行时输出:

Connecting to address 192.168.0.185 port 9090 
    ADDRESS-------------> 0 
    loop-----------------> 2 
    sfd-----------------> 5 
    sts-----------------> 0 
    SOCKET-----------------> 5 

$ ping 192.168.0.185 
PING 192.168.0.185 (192.168.0.185) 56(84) bytes of data. 
From 192.168.0.185 icmp_seq=1 Destination Host Unreachable 
From 192.168.0.185 icmp_seq=2 Destination Host Unreachable 
From 192.168.0.185 icmp_seq=3 Destination Host Unreachable 

我想了解what's会在这里?为什么它将套接字从不存在的IP地址连接到端口?

回答

3

您正在将IPv4地址而不是主机名传递给getaddrinfo()(但您应该在hints.ai_flags字段中指定AI_NUMERICHOST)。它将输出一个包含sockaddr_in的单个addrinfo原样,它不会尝试验证IP的存在。这就是为什么getaddrinfo()将返回0。

你告诉getaddrinfo(),你将要使用UDP(SOCK_DGRAM)插座,而不是TCP(SOCK_STREAM)插槽。因此,输出addrinfo包含调用socket()时用于创建UDP套接字的信息。

然后您在UDP套接字上调用connect()。在UDP中,connect()实际上并不创建物理连接,就像它对TCP一样。它只是将指定的对等IP分配给套接字,以便可以使用send()recv()代替sendto()recvfrom()。这就是为什么connect()返回0而不是失败。这样做允许send()总是将数据包发送到相同的IP,而recv()只接受从相同IP接收的数据包。

您实际上并未发送任何数据,因此对等IP未在您的代码的任何步骤进行验证。一旦开始发送数据,传输的数据包将最终收到来自网络的ICMP主机不可达错误,从而导致send()recv()开始失败。

所以,如果你想connect()失败的一个不可达的IP地址,创建一个TCP套接字,而不是一个UDP套接字。否则,如果您继续使用UDP套接字,则需要将数据发送到IP,以便网络尝试物理地路由它。

顺便说一句,如果connect()确实失败,那么您将泄漏输出addrinfo,因为只有在connect()成功时才调用freeaddrinfo()。无论您如何使用addrinfo数据,您都需要随时拨打freeaddrinfo()getaddrinfo()成功。

+0

很好解释。通过SOCKET_STREAM参数改变套接字使用TCP解决了这个问题。 freeaddrinfo漏洞也解决了。感谢您的帮助! – Mendes

1

UDP“连接”不是网络操作。它在本地API中设置一个条件,它过滤掉来自其他主机的数据报,并允许您使用send()而不是sendto()。它不会失败,但后续发送将会。

相关问题