即使将 SO_REUSEADDR 与 IPv6 一起使用,套接字绑定也会失败 [英] Socket bind fails even after using SO_REUSEADDR with IPv6

查看:16
本文介绍了即使将 SO_REUSEADDR 与 IPv6 一起使用,套接字绑定也会失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序需要在同一个端口上绑定一个具有相同 IPv6 地址的套接字.我正在使用下面的代码来实现相同的目的.但是第二次绑定会引发错误地址已在使用中".顺便说一句,只有当我包含第一个套接字的监听调用时,我才会收到此错误.没有listen调用,第二个socket绑定就好了.

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.

谢谢

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
",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
",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
",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
", 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
", fd2, straddr, addr.sin6_port);

return 0;

推荐答案

来自 socket(7) 手册页:

SO_REUSEADDR - 表示用于验证 bind(2) 调用中提供的地址的规则应该允许重用本地地址.对于 AF_INET 套接字,这意味着套接字可以绑定,除非有一个活动的侦听套接字绑定到该地址.当监听套接字绑定到具有特定端口的 INADDR_ANY 时,就不可能为任何本地地址绑定到该端口.参数是一个整数布尔标志.

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.

(强调我的)

您的第一个套接字绑定到与 INADDR_ANY 等效的 IPv6 上的端口 143,然后它进入侦听状态.因此,只要第一个套接字保持打开状态,就不能在端口 143 上绑定其他套接字.

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.

这篇关于即使将 SO_REUSEADDR 与 IPv6 一起使用,套接字绑定也会失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆