TCP客户端随机端口能否复用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP客户端随机端口能否复用相关的知识,希望对你有一定的参考价值。

参考技术A 结论:

1、理论上是可以复用的,客户端进程需要做一些单独参数设置(reuse)

2、实际上系统默认不复用,只从空闲的端口里面去选择

参考链接:

https://www.zhihu.com/question/342296674

Nodejs 随机免费 tcp 端口

【中文标题】Nodejs 随机免费 tcp 端口【英文标题】:Nodejs random free tcp ports 【发布时间】:2015-03-18 23:11:20 【问题描述】:

每次实例化我的类的新实例时,我的项目都需要设置一个新端口。

在 Node.js 中,我如何找到一个空闲的 TCP 端口来设置我的新套接字服务器?或者检查我指定的端口是否已经被使用。

【问题讨论】:

【参考方案1】:

您可以通过为端口指定0 来绑定到操作系统分配的随机空闲端口。这样你就不会受到竞争条件的影响(例如,在你有机会绑定到它之前检查一个开放的端口和一些绑定到它的进程)。

然后就可以调用server.address().port获取分配的端口了。

例子:

var net = require('net');

var srv = net.createServer(function(sock) 
  sock.end('Hello world\n');
);
srv.listen(0, function() 
  console.log('Listening on port ' + srv.address().port);
);

【讨论】:

这是一个很好的答案,但在这种情况下,如果可以测试端口,我需要使用 try/catch 并重试,不是吗? 如果你想要一个特定的端口,只需尝试监听它并检查错误事件。如果您收到错误消息和error.code === 'EADDRINUSE',那么您就知道该端口正在使用中。 同样适用于http.createServer(...).listen()【参考方案2】:

对于 Express 应用:

const app = require('express')();

const server = app.listen(0, () => 
  console.log('Listening on port:', server.address().port);
);

【讨论】:

【参考方案3】:

要查找打开的 TCP 端口,您可以使用模块 portastic

你可以找到这样的端口:

port = require('portastic');

options = 
    min : 8000,
    max : 8005


port.find(options, function(err, data)
    if(err)
        throw err;
    console.log(data);
);

【讨论】:

【参考方案4】:

端口查找器库:

https://github.com/http-party/node-portfinder

我建议你使用portfinder库,它一周下载量超过1000万。

默认portfinder库将从8000开始搜索并扫描,直到达到最大端口号(65535)。

const portfinder = require('portfinder');

portfinder.getPort((err, port) => 
    //
    // `port` is guaranteed to be a free port
    // in this scope.
    //
);

【讨论】:

不解释就投反对票?【参考方案5】:

等待港口

对于那些试图“同步”执行此操作的人,下游代码依赖于操作系统选择的 port(例如,在创建测试服务器并将端口传递给测试时 ),以下食谱对我很有帮助:

export const sleepForPort = async (httpServer: Server, ms: number): Promise<number> => 
  return new Promise<number>((resolve, reject) => 
    httpServer.listen(0, async () => 
      try
        let addr = (httpServer.address() as AddressInfo | null)
        while(! (addr && addr.port) ) 
          await sleep(ms);
          addr = httpServer.address() as AddressInfo | null
        
        resolve(addr.port);
      catch(e)
        reject(e);
      
    );
  );


const sleep = (ms: number) => 
  return new Promise(resolve => setTimeout(resolve, ms));

如果对实时服务器运行集成测试,这允许我们 await 端口号变为可用,并返回它以便我们的 testClient 可以在此端口上点击 localhost

export const setupTests = async () => 
  const app = createExpressApp(...);
  const httpServer = createServer(app);
  server.installSubscriptionHandlers(httpServer); // Testing graphql subscriptions
  const port = await sleepForPort(httpServer, 100);
  return port


describe("Http server test", () => 
  let port: number;
  beforeAll(async () => 
    port = await setupTests()
  )
  it("Hits the right port", () => 
    const res = await fetch(`http://localhost:$port/testing`)
    expect(res).toBeDefined()
    expect(res.status).toEqual(200);
  
)

【讨论】:

【参考方案6】:

在构建本地运行的应用程序时,我曾多次使用公认的答案 - 感谢@mscdex。但是,我最近有一个用例,如果可能的话,最好坚持使用给定的端口,但如果使用了另一个端口,仍要回退到另一个端口。

这样做的原因是我想使用 localStorage 保存应用程序首选项,它仅限于同一个站点,具有相同的端口以确保安全。因此,我希望尽可能使用端口 3000,但如果需要,可以回退到 3001 或 3002 等,直到有一个可用。如果使用的端口不是 3000,那么我会回退到存储在用户硬盘上的应用首选项副本。

我用这个模式来做这个:

const http = require('http');
const server = http.createServer(function(req,res) ... )

const config = 
  port: 3000,
  launched: false,
;

serverListen(server, config.port);

function serverListen(server, port)
  server.on('error', e => 
    console.log(`port $config.port is taken`)
    config.port +=1;
    server.close();
    serverListen(server, config.port);
  ).listen(port, function() 
    if (config.launched)
      return;
    
    console.log('Listening on port ' + server.address().port);
    launchBrowser();
    config.launched = true;
  );

launchBrowser()只是一个启动浏览器到'http://127.0.0.1:' + server.address().port的函数

该应用程序是一个简单的本地服务器+浏览器应用程序。浏览器是 GUI,服务器在用户硬盘上修改文件等。

【讨论】:

以上是关于TCP客户端随机端口能否复用的主要内容,如果未能解决你的问题,请参考以下文章

FTP服务的主动模式和被动模式

Linux高性能服务编程(I/O复用)

Python-TCP服务端程序开发

史上最全的python的web开发和网络编程附属详细解释+案例

Linux 建立 TCP 连接的超时时间分析

magelinux(0111)