Linux 下常用的命令 lsofnetstat 都可以用来列出端口以及运行在端口上的服务。

验证环境

为了验证这两个命令,在本地的一台 Ununtun 机器上部署了一个简单的 Kafka broker,端口为 9092,局域网 ip 为 192.168.31.188。在我的开发环境中,ip 为 192.168.31.51 启动了一个 kafka 生产者,在每一次生产消息后会进行休眠一段时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class KafkaUtil {

public static void main(String[] args) {
System.out.println("pid:" + getPid());
KafkaProducer<String, String> producer = createProducer();
for (int i = 0; i < 10; i++) {
String time = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
String message = "message at " + time;

System.out.println("message: " + message);
ProducerRecord<String, String> record = new ProducerRecord<>("test", message);
producer.send(record, (r, e) -> {
if (r != null) {
System.out.printf("topic:%s, partition:%s, offset:%s\n", r.topic(), r.partition(), r.offset());
}

if (e != null) {
e.printStackTrace();
}
});

threadSleep(Duration.ofMinutes(10));
}
producer.flush();
}

private static KafkaProducer<String, String> createProducer() {
Properties properties = new Properties();
properties.put("bootstrap.servers", "192.168.31.188:9092");
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("acks", "all");
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
return producer;
}

public static void threadSleep(Duration duration) {
try {
long millis = duration.toMillis();
Thread.sleep(millis);
} catch (InterruptedException e) {
// ignore
}
}

public static int getPid() {
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
String name = runtime.getName(); // format: "pid@hostname"
try {
return Integer.parseInt(name.substring(0, name.indexOf('@')));
} catch (Exception e) {
return -1;
}
}
}

启动时打印出了该程序的进程 PID: pid:23264, 此过程也可以通过 Windos 下的任务管理器查看,如果是 IDEA 中运行的,可以在 进程 的选项卡下的 IntelliJ IDEA 子进程下查看

image-20210815120804600

Windos 下也有 netstat 命令来查看端口占用和相关的进程,我们找到进程 ID 为 23264 的所有连接,可以看到目标列,即为 Kafka 的 broker 监听的地址 (192.168.31.188:9092)

1
2
3
4
5
6
7
8
# -a 显示所有连接和侦听端口 -n 以数字形式显示地址和端口号 -o 显示拥有的与每个连接关联的进程 ID
C:\Users\guo>netstat -ano | findstr "23264"
协议 本地地址 外部地址 状态 PID
TCP 127.0.0.1:50281 127.0.0.1:50280 ESTABLISHED 23264
TCP 127.0.0.1:50282 127.0.0.1:50283 ESTABLISHED 23264
TCP 127.0.0.1:50283 127.0.0.1:50282 ESTABLISHED 23264
TCP 192.168.31.51:50286 192.168.31.188:9092 ESTABLISHED 23264
TCP 192.168.31.51:50288 192.168.31.188:9092 ESTABLISHED 23264

lsof

Linux 下的 lsof, 即 list open files, 列出已打开的文件, -i 选项列出打开的网络接口文件 ( -i select IPv[46] files)。在 kafka broker 所在机器下查看 9092 端口的使用情况,可以找到与之对应的连接:

1
2
3
4
5
6
# -n 以数字形式显示地址
ph@guo-lenovo:~$ sudo lsof -i -n | grep 9092
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 174961 root 95u IPv6 2748921 0t0 TCP *:9092 (LISTEN)
java 174961 root 100u IPv6 2854156 0t0 TCP 192.168.31.188:9092->192.168.31.51:50286 (ESTABLISHED)
java 174961 root 101u IPv6 2854157 0t0 TCP 192.168.31.188:9092->192.168.31.51:50288 (ESTABLISHED)

192.168.31.188:9092->192.168.31.51:50286 箭头前表示本地地址(Source/Local),箭头后表示外部地址(Target/Foreign)

打印所有开放的端口

根据输出的形式,可以以一个简单的脚本实现打印出当前所有的开放端口:

1
sudo lsof -i | grep -Eo ":[0-9a-zA-Z]+->" | grep -Eo "[0-9a-zA-Z]+" | sort | uniq

netstat

Linux 下的 netstat 与 Windos 下的命令效果一样的:

1
2
3
4
5
6
7
8
# -t tcp
# -n, --numeric don't resolve names
# -p, --programs display PID/Program name for sockets
ph@guo-lenovo:~$ netstat -tnp | grep 9092
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp6 0 0 192.168.31.188:9092 192.168.31.51:50288 ESTABLISHED -
tcp6 0 0 192.168.31.188:9092 192.168.31.51:50286 ESTABLISHED -