将套接字绑定到非短暂端口范围的空闲端口

时间:2014-03-24 18:37:41

标签: c++ linux sockets

众所周知,为了将套接字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;
}

P.S。现在我使用的解决方法是明确地将非短暂端口范围内的随机端口号传递为sockaddr_in.sin_port,尝试绑定到它,如果绑定操作失败则进行冲洗重复。但这感觉就像模仿系统应该提供的东西一样。

3 个答案:

答案 0 :(得分:0)

  

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

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

答案 1 :(得分:-2)

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

答案 2 :(得分:-2)

我的回答是假设您的问题中的非短暂性是指特权端口范围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。

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