2

My application needs to bind a socket on the same port with the same IPv6 address. I am using the code below to achieve the same. However second bind throws an error 'Address already in use'. BTW, i get this error only if I include the listen call for the first socket. Without the listen call, the second socket binds just fine.

What I am doing wrong? Please help me understand.

Thanks

int fd1 = ::socket(AF_INET6, SOCK_STREAM, 0);
if (fd1 < 0)
{
    perror("fd1 socket()");
    return -1;
}

// Set SO_REUSEADDR for both sockets
int reuse = 1;
if (fcntl(fd1, F_SETFL, O_RDWR|O_NONBLOCK) <0)
{
    perror("fd1 fcntl64 failed");
    return -1;
}
if (::setsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
    perror("fd1 SO_REUSEADDR failed");
    return -1;
}
if (::setsockopt(fd1, SOL_IPV6, IPV6_V6ONLY, &reuse, sizeof(reuse)) < 0)
{
    perror("fd1 SO_REUSEADDR failed");
    return -1;
}


sockaddr_storage storage;
socklen_t addrlen = sizeof(storage);
memset(&storage, 0, addrlen);
sockaddr_in6& addr = reinterpret_cast<sockaddr_in6&>(storage);
addr.sin6_family = AF_INET6;
addr.sin6_port = 143;
addr.sin6_addr = in6addr_any;
sockaddr* pAddr = reinterpret_cast<sockaddr*>(&storage);

int val = 2;
socklen_t len = sizeof(val);

if (::getsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &val, &len) < 0)
{
    perror("fd1 getsock failed");
    return -1;
}

printf("Getsockopt returned %d at %d\n",val, __LINE__);

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

if (::getsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &val, &len) < 0)
{
    perror("fd1 getsock failed");
    return -1;
}

printf("Getsockopt returned %d at %d\n",val, __LINE__);
if (listen(fd1, 128) < 0)
{
    perror("fd1, listen");
        return -1;
}
if (::getsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &val, &len) < 0)
{
    perror("fd1 getsock failed");
    return -1;
}

printf("Getsockopt returned %d at %d\n",val, __LINE__);
// Get the local address for fd1
addrlen = sizeof(storage);
if (::getsockname(fd1, pAddr, &addrlen))
{
    perror("getsockname for fd1 failed");
    return -1;
}
char straddr[INET6_ADDRSTRLEN];
if (!inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)))
{
    perror("inet_ntop for fd1 failed");
    return -1;
}
printf("fd1=%d addr=%s:%d\n", fd1, straddr, addr.sin6_port);

addrlen = sizeof(storage);
addr.sin6_family = AF_INET6;
addr.sin6_port = 143;
int fd2 = ::socket(AF_INET6, SOCK_STREAM, 0);
if (fd2 < 0)
{
    perror("fd2 socket()");
    return -1;
}
if (::setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
    perror("fd2 SO_REUSEADDR failed");
    return -1;
}
if (::setsockopt(fd2, SOL_IPV6, IPV6_V6ONLY, &reuse, sizeof(reuse)) < 0)
{
    perror("fd1 SO_REUSEADDR failed");
    return -1;
}

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

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

return 0;
4

1 に答える 1

3

From the socket(7) man page:

SO_REUSEADDR - Indicates that the rules used in validating addresses supplied in a bind(2) call should allow reuse of local addresses. For AF_INET sockets this means that a socket may bind, except when there is an active listening socket bound to the address. When the listening socket is bound to INADDR_ANY with a specific port then it is not possible to bind to this port for any local address. Argument is an integer boolean flag.

(emphasis mine)

Your first socket is bound to port 143 on the IPv6 equivalent of INADDR_ANY, then it's put in listening state. Thus no other socket could be bound on port 143 as long as the first socket remains open.

于 2012-07-27T11:11:41.333 に答える