本文发布于Cylon的收藏册,转载请著名原文链接~

方法1:dial

使用 net.DialTimeout 去检查端口的技巧:

在通过Dial检查端口占用时,需要知道网络中常见的报错状态,而不是 err != nil 都为可用

Connection reset by peer

connection reset by peer 这种错误情况下有以下几种场景:

  • 基于包过滤的防火墙给予 RST;对于此情况,基于网络模型来说处于网络层与传输层之间的netfilter,如果是防火墙拒绝那么未到应用层无法确认端口
  • 对端应用资源限制而reset,通常为负载过高;对于此场景是已到达应用层
  • 客户端关闭了连接,而服务器还在给客户端发送数据;对于端口检查来说不会到这步

由上面可知,这种错误一定为占用

Connection timed out

Connection timed out 这种场景根本就dial不成功,go中给出了一个专门的事件 opErr.Timeout() 来说明这个错误,故此错误将不能确认端口是否占用

Connection refused

Connection refused 这种场景催在两种情况

  • 对于 local 场景来说,这将表示端口未监听
  • 对于远端场景来说,这种基本上表示 client 发往 remote ,remote不能接受 host:port 这个连接

通常对于存在两种情况,但多数为端口为监听

  1. Misconfiguration, such as where a user has mistyped the port number, or is using stale information about what port the service they require is running on.
  2. A service error, such as where the service that should be listening on a port has crashed or is otherwise unavailable.

所以此状态可以用于判断端口的状态,而对于端口检测通常为 local,所以可以用作判断依据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func checkPortIsAvailable(protocol string, port int) {
	timeoutSecs := 3
	addr, err := GetInterfaceIpv4Addr("eth0")
	conn, err := net.DialTimeout(protocol, net.JoinHostPort(addr, strconv.Itoa(port)), time.Duration(timeoutSecs)*time.Second)
	for {
		if err != nil {
			opErr, ok := err.(*net.OpError)
			if ok && strings.Contains(opErr.Err.Error(), "refused") {
				break
			} else if opErr.Timeout() {
				continue
			} else {
				continue
			}
		}
		
		if conn != nil {
			defer conn.Close()
			continue
		}
	}
}

方法2:golib

github.com/antelman107/net-wait-go 可以用于等待端口直到状态为open,通过这种方法也可以很好的检测端口是否占用

本文发布于Cylon的收藏册,转载请著名原文链接~

链接:https://www.oomkill.com/2023/01/goskill-port-is-available/

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。