Node.js 探测 TCP 端口

在网络技术中,端口(Port)大致有两种意思:

  • 物理意义上的端口:比如,ADSL Modem/集线器/交换机/路由器上用于连接其他网络设备的接口,如RJ-45端口、SC端口等等
  • 逻辑意义上的端口:一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80/443。

而在实际开发过程中,经常遇到一些关于跟端口有关的问题,比如运行服务时提示:端口已被占用。

这篇文章的目的是为了解决运行服务的时候端口被占用的情况,通过代码来检测某个端口是否已经被占用,也是目前进行端口探测的主要手段。

下面跟大家分享研究的代码片段,有需要的可以参考借鉴。

通过服务监听的方式

原理就是创建一个web服务,然后监听指定端口,通过能否监听指定端口来判断是否已经被其它服务占用。

这种方式有点像“以毒攻毒”,既然监听端口可能会出现端口被占用的情况,那也可以用来判断这个端口是否可用的依据。

const net = require('net')
const detector = new net.Server()
detector.on('listening', () => {
  console.log(`this port is available`)
  detector.close(true)
})
detector.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.log('this port is not available')
  }
  throw err
})
detector.listen(80)

不过,这个方法的缺点也是有的,通过创建服务监听端口的方式会影响正常服务的创建

下面的代码,web服务一定无法启动。

const net = require('net')
const detector = new net.Server()
detector.on('listening', () => {
  console.log(`this port is available`)
  detector.close(true)
})
detector.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.log('this port is not available')
  }
  throw err
})
detector.listen(80)

const web = new net.Server()
web.on('listening', () => {
  console.log('web server is available')
})
web.on('error', (err) => {
  console.log('web server has error', err)
})
web.listen(80)

 

通过Socket连接方式

既然通过端口监听的方式可能会造成正常服务无法启动,那我们换一种思路。即不通过提供服务的方式,而是通过客户端连接的方式来判断指定端口是否可用。

const net = require('net')
const detector = new net.Socket()
detector.on('connect', () => {
  console.log(`this port is not available`)
  detector.destroy()
})
detector.on('error', (err) => {
  if (err.code === 'ECONNREFUSED') {
    return console.log('this port is available')
  }
  throw err
})
detector.connect(80)

这种方式就是把自己当成客户端,然后去尝试连接指点端口,如果能成功建立socket连接,那这个端口一定被其它服务占用了,否则端口可用。

而且,这种方式也不会造成正常服务启动受阻,可以说十分可靠。

const net = require('net')
const detector = new net.Socket()
detector.on('connect', () => {
  console.log(`this port is not available`)
  detector.destroy()
})
detector.on('error', (err) => {
  if (err.code === 'ECONNREFUSED') {
    return console.log('this port is available')
  }
  throw err
})
detector.connect(80)

const web = new net.Server()
web.on('listening', () => {
  console.log('web server is available')
})
web.on('error', (err) => {
  console.log('web server has error', err)
})
web.listen(80)

总结

端口探测基本就是两种方式,一种通过创建服务监听端口的方式,一种是模拟客户端进行连接方式。

从实用性角度上来说,推荐大家使用第二种,因为第一种存在阻碍正常服务启动的可能性。