2012-02-21 21 views
3

私は2つのUDPソケットを作成し、ポート0(一時的なポートを割り当てるためにスタックを要求する)でループバックアドレスにバインドするのに問題があります。私の理解では、両方のソケットが異なるポートにある必要があります。以下のコード例では、両方のソケットが同じIPアドレスとポートにあると報告されています。バインドで同じエフェメラルポートが返されるのはなぜですか?

#include <stdio.h> 
#include <arpa/inet.h> 

int main(int, char**) 
{ 
    int fd1 = ::socket(AF_INET, SOCK_DGRAM, 0); 
    if (fd1 < 0) 
    { 
     perror("fd1 socket()"); 
     return -1; 
    } 
    int fd2 = ::socket(AF_INET, SOCK_DGRAM, 0); 
    if (fd2 < 0) 
    { 
     perror("fd2 socket()"); 
     return -1; 
    } 

    // Set SO_REUSEADDR for both sockets 
    int reuse = 1; 
    if (::setsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) 
    { 
     perror("fd1 SO_REUSEADDR failed"); 
     return -1; 
    } 
    if (::setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) 
    { 
     perror("fd2 SO_REUSEADDR failed"); 
     return -1; 
    } 

    sockaddr_storage storage; 
    socklen_t addrlen = sizeof(storage); 
    sockaddr_in& addr = reinterpret_cast<sockaddr_in&>(storage); 
    addr.sin_family = AF_INET; 
    addr.sin_port = 1234; 
    addr.sin_port = 0; 
    if (::inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0) 
    { 
     perror("Failed to create address 127.0.0.1"); 
     return -1; 
    } 
    sockaddr* pAddr = reinterpret_cast<sockaddr*>(&storage); 

    if (::bind(fd1, pAddr, addrlen) < 0) 
    { 
     perror("bind fd1 failed"); 
     return -1; 
    } 

    // Get the local address for fd1 
    addrlen = sizeof(storage); 
    if (::getsockname(fd1, pAddr, &addrlen)) 
    { 
     perror("getsockname for fd1 failed"); 
     return -1; 
    } 
    char straddr[INET_ADDRSTRLEN]; 
    if (!inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr))) 
    { 
     perror("inet_ntop for fd1 failed"); 
     return -1; 
    } 
    printf("fd1=%d addr=%s:%d\n", fd1, straddr, addr.sin_port); 

    if (::bind(fd2, pAddr, addrlen) < 0) 
    { 
     perror("bind fd2 failed"); 
     return -1; 
    } 

    // Get the local address for fd2 
    addrlen = sizeof(storage); 
    if (::getsockname(fd2, pAddr, &addrlen)) 
    { 
     perror("getsockname for fd2 failed"); 
     return -1; 
    } 
    if (!inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr))) 
    { 
     perror("inet_ntop for fd2 failed"); 
     return -1; 
    } 
    printf("fd2=%d addr=%s:%d\n", fd2, straddr, addr.sin_port); 

    return 0; 
} 

このコードは次のような出力を提供します...

fd1=4 addr=127.0.0.1:1933 
fd2=5 addr=127.0.0.1:1933 

私は同じ(ローカル)IPアドレスが、別のポートで両方のソケットが必要です。なぜ、両方のソケットが同じポートを共有しているのか誰も説明できますか?誰でも修正案を提案できますか?

答えて

2

これはUDPソケットでのSO_REUSEADDRの予想される動作です。その設定を削除して、通常の割り当てルールに戻ります。

+0

SO_REUSEADDRオプションではありません。これを削除すると、2番目のバインドが失敗することを意味します。 2つのUDPソケットを同じインターフェイスにバインドし、異なるポートを持つことができるはずです。 – Drew

+0

また、fd2のバインドをfd1のバインドの直下に移動すると、別のポートが取得されることにも言及する必要があります。非常に奇妙な。私はこの行動を説明することを忘れています。 – Drew

+2

これは、fd1をバインドした後、 'paddr'を使って' getsockname() 'を呼び出すためです。したがって、2番目のバインドは、fd1に割り当てられたものと同じポート番号を要求しています。 'SO_REUSEADDR'を設定すると、それが許されます。これはあなたが望むものではありませんが。 2回目のバインドの前に 'paddr'を必ずリセットしてください。 –