【问题标题】:Bind socket to a free port from non-ephemeral port range将套接字绑定到非临时端口范围内的空闲端口
【发布时间】:2014-03-24 18:37:40
【问题描述】:

众所周知,为了将套接字 fd 绑定到随机空闲端口,您将 0 作为 sockaddr_in.sin_port 传递给 bind。但是,这似乎总是从临时端口范围内分配一个端口,如下面的程序 [*] 所示。

我的问题是,有没有一种 [干净的] 方法可以从 非临时 端口范围获取随机端口?

[*]

#include <fstream>
#include <iostream>

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

using namespace std;

int main(int argc, char* argv[]) {
  fstream ports("/proc/sys/net/ipv4/ip_local_port_range", fstream::in);
  int eMin, eMax; ports >> eMin >> eMax;
  cout << "ephemeral range: [" << eMin << ", " << eMax << "]" << endl;
  for (int i = 0; i < 100; ++i) {
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    socklen_t addrlen = sizeof(addr);
    bind(fd, (sockaddr*)(&addr), addrlen);
    getsockname(fd, (sockaddr*)(&addr), &addrlen);
    uint16_t port = ntohs(addr.sin_port);
    bool within = (port >= eMin) && (port <= eMax);
    cout << "port " << port << " " << (within ? "in" : "out") << endl;
    close(fd);
  }
  return 0;
}

附:现在我正在使用的解决方法是从非临时端口范围显式传递一个随机端口号为sockaddr_in.sin_port,尝试绑定到它,如果绑定操作失败,则重复冲洗。但这感觉像是在模仿系统应该提供的东西。

【问题讨论】:

  • 我不知道是否有通用方法,但是在例如 linux 上,您可以从 /proc/net 伪文件中知道正在使用哪些端口,因此大概选择一个未使用的端口少一点盲目的试错。
  • 出于什么目的?你为什么关心价值是什么?如果这样做,为什么不能使用固定值?
  • 我没有看到任何迹象表明这是可能的,而且这是有道理的,因为他们对 ephemeral 的定义本质上是“没人会去的数字”关心,所以可以不假思索地分发。”
  • 无法重现...您使用哪个操作系统?

标签: c++ linux sockets


【解决方案1】:

现在我正在使用的解决方法是从非临时端口范围显式传递一个随机端口号为sockaddr_in.sin_port,尝试绑定到它,如果绑定操作失败则冲洗重复。但这感觉像是在模仿系统应该提供的东西。

遗憾的是,这正是您需要做的。没有标准或特定于平台的 API 可用于从一系列非临时端口中选择可用的随机端口。操作系统只知道 临时 端口,因为该范围是其网络配置的一部分。

【讨论】:

    【解决方案2】:

    典型 TCP/IP 堆栈中的源端口分配并不是特别复杂。至少 BSD TCP/IP 堆栈相当简单。如果您显式绑定到特定端口号,则堆栈要么允许,要么不允许。如果您不绑定到特定地址,堆栈会从临时端口范围分配一个。至少在 BSD 堆栈中,自动端口分配总是发生在临时端口范围内。我怀疑 TCP/IP 堆栈在 Linux 中的行为与标准的 bind() 行为非常相似。

    【讨论】:

    • 这如何回答所提出的问题?
    • 答案是在 BSD TCP/IP 堆栈中没有“[clean] 方法从非临时端口范围获取随机端口”,其中“clean” == “由堆栈提供”。显然,要证明在 TCP/IP 的所有实现中都没有干净的方法是相当困难的。
    • @ArtemB:如果网络堆栈提供了一个选项,比如setsockopt() 的自定义操作码,可以在每个套接字的基础上设置用户定义的端口范围,@987654322 @ 然后可以搜索而不是操作系统的默认临时范围。哦,好吧,我们只能做梦……
    【解决方案3】:

    我的回答假设您的问题中的 non-ephemeral 是指特权端口范围 1-1024。如果您指的是其他范围,请相应地调整命令中的数字。

    在 Linux 上,您可以非常“干净”地执行此操作,但它会对 所有 本地应用程序生效。我确实意识到,严格来说,这只会使这个新范围成为临时端口范围......

    awk '{print $2}' /etc/services |awk -F"/" '{if ($1 && $1 < 1025) print $1}'|tr '\n' ', '|sudo tee /proc/sys/net/ipv4/ip_local_reserved_ports > /dev/null
    echo "1 1024"|sudo tee /proc/sys/net/ipv4/ip_local_port_range >/dev/null
    

    第一行将可能正在使用的特权端口添加到选择中未使用的端口列表中。第二行将本地端口选择的范围设置为 1-1024。

    如果您希望它是特定于应用程序的,我无法帮助您。也许使用网络命名空间和该应用程序的专用接口可能是可能的,但我不知道具体如何。

    【讨论】:

    • 这会在一个范围内找到一个空闲端口,但不会将套接字绑定到它,当您绑定到它时,您可能会发现该端口在此期间已被占用。时间窗问题。不是一个实用的解决方案,也不是对 OP 已经在做的事情的改进,不管他为什么这样做。
    • @EJP 你是一个真正的甜心,让这些标签中的生活变得如此光明。
    猜你喜欢
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 2011-08-19
    • 1970-01-01
    • 2020-11-24
    • 1970-01-01
    相关资源
    最近更新 更多