linux下获取进程网络链接状况(包括打开的侦听端口号)

来源:互联网 发布:数据录入 招聘 编辑:程序博客网 时间:2024/05/28 01:36

在做一个本地服务(服务器端和客户端在同一个机器上,通过回环端口直接访问)时,因为端口可能会被其他程序占用,服务器端会尝试找到一个可用端口,客户端需要逐个尝试连接各个端口,并且与服务器端完成一个验证过程,才能最终确认服务器端的侦听端口。即使双方可以约定从某个端口开始,每次加1尝试,提高发现效率,但是实现起来还是要费一番心事的。

有没有办法直接获得服务器端程序侦听的端口号呢?

我们知道netstat 能够输出socket信息,并且当指定-p选项时,可以输出该socket所属进程的pid和名称,另一个命令 lsof 可以输出进程打开的文件描述符的信息,既然他们能够获取到这些信息,那就应该有办法获取获得服务器端程序侦听的端口号。

没有这两个命令的源代码,那就用 strace 破解吧,用 strace lsof -p 16805 2> log 将lsof调用的系统函数过程保存起来。

lsof 的输出结果最后几行显示了侦听端口,分别是9006、9003:

test 16805 root    8u  0000    0,9        0      963 anon_inodetest 16805 root    9r  FIFO    0,8      0t0   127986 pipetest 16805 root   10w  FIFO    0,8      0t0   127986 pipetest 16805 root   11u  0000    0,9        0      963 anon_inodetest 16805 root   12r  FIFO    0,8      0t0   127987 pipetest 16805 root   13w  FIFO    0,8      0t0   127987 pipetest 16805 root   14u  IPv4 127999      0t0      TCP *:9006 (LISTEN)test 16805 root   15u  IPv4 127992      0t0      TCP *:9003 (LISTEN)

打开log文件,其中与16805这个进程相关的部分如下:

open("/proc/16805/fd", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 4getdents64(4, /* 18 entries */, 32768)  = 432readlink("/proc/16805/fd/0", "/dev/pts/0"..., 4096) = 10lstat64("/proc/16805/fd/0", {st_mode=S_IFLNK|0700, st_size=64, ...}) = 0stat64("/proc/16805/fd/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0open("/proc/16805/fdinfo/0", O_RDONLY|O_LARGEFILE) = 7fstat64(7, {st_mode=S_IFREG|0400, st_size=0, ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78a6000read(7, "pos:\t0\nflags:\t0100002\n", 1024) = 22close(7)                                = 0munmap(0xb78a6000, 4096)                = 0readlink("/proc/16805/fd/1", "/dev/pts/0", 4096) = 10lstat64("/proc/16805/fd/1", {st_mode=S_IFLNK|0700, st_size=64, ...}) = 0stat64("/proc/16805/fd/1", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0open("/proc/16805/fdinfo/1", O_RDONLY|O_LARGEFILE) = 7fstat64(7, {st_mode=S_IFREG|0400, st_size=0, ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78a6000read(7, "pos:\t0\nflags:\t0100002\n", 1024) = 22close(7)                                = 0munmap(0xb78a6000, 4096)                = 0readlink("/proc/16805/fd/2", "/dev/pts/0", 4096) = 10lstat64("/proc/16805/fd/2", {st_mode=S_IFLNK|0700, st_size=64, ...}) = 0stat64("/proc/16805/fd/2", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0open("/proc/16805/fdinfo/2", O_RDONLY|O_LARGEFILE) = 7fstat64(7, {st_mode=S_IFREG|0400, st_size=0, ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78a6000

可以看到,lsof 对 /proc/16805/fd 目录下的所有文件都调用了readlink,又读入 /proc/16805/fdinfo 目录下的所有文件。

fd目录下的文件都是符号链接,用ls -l /proc/16805/fd 可以看到侦听套接字显示为socket:[127999],但是[]里面的数字代表什么呢?

l-wx------ 1 zenzhang zenzhang 64 2012-07-24 14:48 10 -> pipe:[127986]lrwx------ 1 zenzhang zenzhang 64 2012-07-24 14:48 11 -> anon_inode:[eventpoll]lr-x------ 1 zenzhang zenzhang 64 2012-07-24 14:48 12 -> pipe:[127987]l-wx------ 1 zenzhang zenzhang 64 2012-07-24 14:48 13 -> pipe:[127987]lrwx------ 1 zenzhang zenzhang 64 2012-07-24 14:48 14 -> socket:[127999]lrwx------ 1 zenzhang zenzhang 64 2012-07-24 14:48 15 -> socket:[127992]

lsof 的 trace log 里面内容比较多,我们通过open作为关键字过滤一下:grep open log,发现中间还打开的其他一些文件:

open("/proc/16805/fdinfo/13", O_RDONLY|O_LARGEFILE) = 7open("/proc/16805/fdinfo/14", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/ax25", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)open("/proc/net/ipx", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)open("/proc/net/raw", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/packet", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/unix", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/raw6", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/sockstat6", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/tcp6", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/udp6", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/udp6lite", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)open("/proc/net/sockstat", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/tcp", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/udp", O_RDONLY|O_LARGEFILE) = 7open("/proc/net/udplite", O_RDONLY|O_LARGEFILE) = 7open("/proc/16805/fdinfo/15", O_RDONLY|O_LARGEFILE) = 7open("/proc/16919/stat", O_RDONLY|O_LARGEFILE) = 4

看来秘密就在 /proc/net 下面的这些文件上了,我们侦听的是tcp套接字,先看看 /proc/net/tcp(cat /proc/net/tcp)

  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                        0: 00000000:2328 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1006        0 128682 1 c53617c0 300 0 0 2 -1   1: 0100007F:232A 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1000        0 149130 1 f0fbb900 300 0 0 2 -1   2: 00000000:070A 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1006        0 128694 1 c5361300 300 0 0 2 -1   3: 0100007F:0CEA 00000000:0000 0A 00000000:00000000 00:00000000 00000000   118        0 5207 1 efcd17c0 300 0 0 2 -1   4: 00000000:232B 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1006        0 127992 1 f0fbaf80 300 0 0 2 -1   5: 0100007F:200B 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1000        0 35424 1 efcd2ac0 300 0 0 2 -1                                6: 00000000:232E 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1006        0 127999 1 f0fbc740 300 0 0 2 -1                               7: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 3597 1 f0fb8000 300 0 0 2 -1                                 8: 00000000:0015 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 7372 1 f0fb8e40 300 0 0 2 -1                                 9: 00000000:8E16 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 3893 1 efcd04c0 300 0 0 2 -1                                10: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 3822 1 efcd0000 300 0 0 2 -1                                11: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 108473 1 f0fbf6c0 300 0 0 2 -1                              12: 00000000:A63A 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 4600 1 efcd0e40 300 0 0 2 -1                                13: 00000000:EC20 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 4664 1 f0fb84c0 300 0 0 2 -1                                14: 00000000:0801 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 4580 1 efcd0980 300 0 0 2 -1                                15: 6401A8C0:0016 D82DA8C0:F083 01 00000000:00000000 02:0009590E 00000000     0        0 151552 2 c5362600 31 4 3 4 -1                               16: 6401A8C0:0016 D12DA8C0:0534 01 00000044:00000000 01:00000026 00000000     0        0 59851 4 efcd3440 39 4 11 5 6                                17: 0100007F:232A 0100007F:EB6E 08 00000000:00000000 00:00000000 00000000  1000        0 149133 1 c5360000 20 4 0 2 -1                               18: 6401A8C0:0016 382DA8C0:0633 01 00000000:00000000 02:0000DA5D 00000000     0        0 35227 2 efcd2600 22 4 0 4 4                                 19: 6401A8C0:0016 43C9A8C0:237F 01 00000000:00000000 02:00034FE4 00000000     0        0 109890 2 efcd1300 34 4 30 4 3                               20: 6401A8C0:0016 DD01A8C0:79EA 01 00000000:00000000 02:00085BE7 00000000     0        0 126031 2 efcd5580 22 4 12 17 16                             21: 6401A8C0:0016 392DA8C0:C3C8 01 00000000:00000000 02:00094AF5 00000000     0        0 7952 2 efcd2140 42 4 26 4 3              

果然,里面有那个奇怪的数字"127999"(红色行),根据说明,第一列是本地地址端口(local_address),那应该就是侦听端口了,00000000:232E,16机制转换后果然是9006。

所以通过下列步骤,可以获取进程打开的侦听端口号:

  1. 枚举 /proc 目录,通过进程文件名找到进程id,用 /proc/<pid>/exe 符号链接匹配文件名
  2. 枚举 /proc/<pid>/fd 目录,找到进程打开的所有文件描述符(符号链接),其中链接内容为socket:[id]的就是连接套接字,读取其id
  3. 读取 /proc/net/tcp 文件,找到第9列内容等于套接字id的的行
  4. 读取local_address列,获得端口号,如果远端地址(rem_address)全是0,就是侦听套接字
	
				
		
原创粉丝点击