macos - SO_REUSEADDR 和 SO_REUSEPORT 的行为改变了吗?

标签 macos sockets

在旧版本的 Mac OS X 中,通配符绑定(bind)的工作方式如下所述:

Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems?

Multicast Addresses

The meaning of SO_REUSEADDR changes for multicast addresses as it allows multiple sockets to be bound to exactly the same combination of source multicast address and port. In other words, for multicast addresses SO_REUSEADDR behaves exactly as SO_REUSEPORT for unicast addresses. Actually the code treats SO_REUSEADDR and SO_REUSEPORT identically for multicast addresses, that means you could say that SO_REUSEADDR implies SO_REUSEPORT for all multicast addresses and the other way round.

MacOS X

At its very core, MacOS X is simply a BSD-style UNIX, based on a rather late fork of the BSD code, which was even synchronized with FreeBSD 5 for the Mac OS 10.3 release. That's why MacOS X offers the same options as BSD and they also behave the same way as in BSD.

但是在 10.10.5 中,我在测试网络库时发现了一个变化。

即使设置了 SO_REUSEADDR,两个未绑定(bind)(通配符)UDP 套接字也不能再共享同一端口 (errno=EADDRINUSE)。 两者都必须设置 SO_REUSEPORT,这对我来说是个谜。

它可以用这个简单的测试代码重现:

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

int main() {
    for(int p = 0; p < 4; ++p) {
        printf("Flags set: ");
        if(p&1) printf("SO_REUSEADDR ");
        if(p&2) printf("SO_REUSEPORT");
        printf("\n");

        int handles[2];
        bool success = true;
        for(int i = 0; i < sizeof(handles)/sizeof(int); ++i) {
            handles[i] = socket(AF_INET, SOCK_DGRAM, 0);

            int flag = 1;
            if((p&1) && setsockopt(handles[i], SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
                printf("Setsockopt %d, SO_REUSEADDR failed with errno\n", i, errno);
                success = false;
                break;
            }
            if((p&2) && setsockopt(handles[i], SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)) == -1) {
                printf("Setsockopt %d, SO_REUSEPORT failed with errno\n", i, errno);
                success = false;
                break;
            }

            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            addr.sin_family = AF_INET;
            addr.sin_port = 2000; // does not matter as long as it is currently free
            addr.sin_addr.s_addr = 0; // wildcard address

            if(bind(handles[i], (struct sockaddr*)&addr, sizeof(addr)) == -1) {
                printf("Bind %d failed with errno %d\n", i, errno);
                success = false;
                break;
            }
        }
        if(success)
            printf("Alright\n");

        for(int i = 0; i < sizeof(handles)/sizeof(int); ++i)
            close(handles[i]);
        printf("\n");
    }

    return 0;
}

哪些输出:

Flags set: 
Bind 1 failed with errno 48

Flags set: SO_REUSEADDR 
Bind 1 failed with errno 48

Flags set: SO_REUSEPORT
Alright

Flags set: SO_REUSEADDR SO_REUSEPORT
Alright

最佳答案

In older versions of Mac OS X, wildcard binding works like described here:

Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems?

Multicast Addresses

您引用的描述适用于多播地址。您的测试代码不使用 multicast address 。因此,适用不同的描述(来自同一来源):

SO_REUSEPORT

SO_REUSEPORT is what most people would expect SO_REUSEADDR to be. Basically, SO_REUSEPORT allows you to bind an arbitrary number of sockets to exactly the same source address and port as long as all prior bound sockets also had SO_REUSEPORT set before they were bound. …

您的测试代码完全证实了这一点。

关于macos - SO_REUSEADDR 和 SO_REUSEPORT 的行为改变了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32661091/

相关文章:

sockets - 是否为每个 UDP 连接打开一个新套接字?

.net - 如何在.Net套接字中监听多个地址?

objective-c - 目前支持的提权方式

MySQL 使用 DUPLICATE KEY UPDATE 时出错

macos - mesos slave 执行 docker 命令失败

java - 现有 java 应用程序上的 SO_KEEPALIVE

java - 基本TCP通讯端口相关查询

python - 如何在 Mac OS X 中为 python3 安装密码学?

android - MacOS 上的 Expo 可以通过 XDE 通过 USB 电缆在我的 Android 设备上打开我的应用程序,但不能通过命令行

java - 使用线程池的客户端/服务器连接?