【发布时间】:2019-01-30 13:20:22
【问题描述】:
考虑这段代码:
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#define SERVADDR "::1"
#define PORT 12345
int main() {
int sd = -1;
if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "socket() failed: %d", errno);
exit(1);
}
int flag = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
fprintf(stderr, "Setsockopt %d, SO_REUSEADDR failed with errno %d\n", sd, errno);
exit(2);
}
if(setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)) == -1) {
fprintf(stderr, "Setsockopt %d, SO_REUSEPORT failed with errno %d\n", sd, errno);
exit(3);
}
struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(23456);
if(bind(sd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
fprintf(stderr, "Bind %d failed with errno %d: %s\n", sd, errno, strerror(errno));
exit(4);
}
struct sockaddr_in6 server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin6_family = AF_INET6;
inet_pton(AF_INET6, SERVADDR, &server_addr.sin6_addr);
server_addr.sin6_port = htons(PORT);
if (connect(sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
fprintf(stderr, "Connect %d failed with errno %d: %s\n", sd, errno, strerror(errno));
exit(5);
}
printf("Seems like it worked this time!\n");
close(sd);
}
很简单:
- 创建套接字
- 设置
SO_REUSEADDR - 设置
SO_REUSEPORT - 绑定本地端口
23456 - 在端口
12345上连接到::1
奇怪的是,在 MacOS 上连续运行会导致以下情况:
$ for i in {1..5}; do ./ipv6; done
Seems like it worked this time!
Connect 3 failed with errno 48: Address already in use
Connect 3 failed with errno 48: Address already in use
Connect 3 failed with errno 48: Address already in use
Connect 3 failed with errno 48: Address already in use
$
虽然在 Linux 上运行它似乎工作得很好:
$ for i in {1..5}; do ./ipv6; done
Seems like it worked this time!
Seems like it worked this time!
Seems like it worked this time!
Seems like it worked this time!
Seems like it worked this time!
$
我在端口 12345 上有一个监听器:
$ nc -6 -l -v -p12345 -k
这不仅限于 IPv6,尝试了与 IPv4 相同的事情 - 相同的行为。
谁能解释一下?
我之前认为它在 bind() 中失败,但它在 connect() 中。
编辑#1
根据this - 适用于BSD:
因此,如果您将相同协议的两个套接字绑定到相同的源地址和端口,并尝试将它们都连接到相同的目标地址和端口,connect() 实际上将失败并出现错误EADDRINUSE for您尝试连接的第二个套接字,这意味着已经连接了具有五个值的相同元组的套接字。
所以这是有道理的,为什么这不起作用。如果这怎么可能在 Linux 上实际运行,那有什么没有意义的呢?
我当然希望在 MacOS 上完成这项工作,但我目前觉得这可能是不可能的 - 但我仍然想了解 Linux 是如何做到的。
【问题讨论】:
标签: c macos sockets raw-sockets