2016-04-12 1 views
0

非existant IPアドレス上のポートに接続しますソケットは、私は次のコードは、Ubuntuの15.10で動作しているサーバに接続ソケットために使用している

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フィールドに指定する必要があります)。そのIPアドレスのためにsockaddr_inを含む単一のaddrinfoを出力するつもりです、それはIPの存在を検証しようとしません。 getaddrinfo()はあなたがUDP(SOCK_DGRAM)ソケットではなく、TCP(SOCK_STREAM)ソケットを使用しようとしているgetaddrinfo()を言っている0

を返す理由です。したがって、出力addrinfoには、socket()を呼び出すときにUDPソケットを作成するための情報が含まれています。

UDPソケットでconnect()を呼び出しています。 UDPの場合、connect()はTCPの場合のように実際に物理接続を作成しません。指定されたピアIPをソケットに割り当てるだけで、sendto()recvfrom()の代わりにsend()recv()を使用できます。そのため、connect()は失敗する代わりに0を返します。これにより、send()は常に同じIPにパケットを送信でき、recv()は同じIPから受信したパケットだけを受け入れることができます。

実際にデータを送信していないため、コードのどのステップでもピアIPが検証されていません。データの送信を開始すると、送信されたパケットはネットワークからのICMPホスト到達不能エラーを受信し、send()recv()が失敗し始めます。

したがって、到達不可能なIPアドレスに対してconnect()が失敗するようにするには、UDPソケットの代わりにTCPソケットを作成します。それ以外の場合、UDPソケットを引き続き使用する場合は、ネットワークが物理的にルーティングするようにデータをIPに送信する必要があります。

connect()が失敗した場合はconnect()が成功した場合にのみ、freeaddrinfo()と呼び出したときに出力がaddrinfoになります。 addrinfoデータの使用方法にかかわらず、getaddrinfo()が成功するといつでもfreeaddrinfo()に電話する必要があります。

+0

非常によく説明されています。 SOCKET_STREAMパラメータでTCPを使用するようにソケットを変更しました。 'freeaddrinfo'漏れも解決されました。助けてくれてありがとう! – Mendes

1

UDP 'connect'はネットワーク操作ではありません。ローカルAPIにcondtionを設定し、他のホストからのデータグラムを除外し、sendto()の代わりにsend()を使用できるようにします。それは失敗しませんが、後続の送信が行われます。