在从 bash 翻译一些脚本时,我遇到了许多使用 netstat -an 来查找我们的服务之一是否正在侦听的情况。虽然我知道我可以只使用 subprocess.call 或其他甚至 popen 我宁愿使用 pythonic 解决方案,所以我没有利用我们正在运行的 unix 环境。
从我读到的内容来看,套接字模块应该有一些东西,但我还没有看到任何检查监听端口的东西。可能是我不理解一个简单的技巧,但到目前为止我知道如何连接到套接字,并编写一些东西让我知道连接何时失败。但我不一定找到了专门检查端口以查看其是否正在监听的东西。
有什么想法吗?
尝试连接怎么样...
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = s.connect_ex(('127.0.0.1', 3306))
if result == 0:
print('socket is open')
s.close()
我知道这个问题很旧,但我是为初学者写的。如果您想识别系统上的监听端口,可以使用下面的代码。
from socket import *
Port = 0 #First port.
while Port <= 65535: #Port 65535 is last port you can access.
try:
try:
Socket = socket(AF_INET, SOCK_STREAM, 0) #Create a socket.
except:
print("Error: Can't open socket!\n")
break #If can't open socket, exit the loop.
Socket.connect(("127.0.0.1", Port)) #Try connect the port. If port is not listening, throws ConnectionRefusedError.
Connected = True
except ConnectionRefusedError:
Connected = False
finally:
if(Connected and Port != Socket.getsockname()[1]): #If connected,
print("{}:{} Open \n".format("127.0.0.1", Port)) #print port.
Port = Port + 1 #Increase port.
Socket.close() #Close socket.
在 Linux 上我们可以使用 strace 来查看 netstat -ln 正在读取并 解析 /proc 文件系统中的各种值。
$ strace netstat -ln 2>&1| grep '/proc'
open("/proc/net/tcp", O_RDONLY) = 3
open("/proc/net/tcp6", O_RDONLY) = 3
open("/proc/net/udp", O_RDONLY) = 3
open("/proc/net/udp6", O_RDONLY) = 3
open("/proc/net/raw", O_RDONLY) = 3
open("/proc/net/raw6", O_RDONLY) = 3
open("/proc/net/unix", O_RDONLY) = 3
open("/proc/net/ipx/socket", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/ipx", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/ax25", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/x25", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/x25", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/nr", O_RDONLY) = -1 ENOENT (No such file or directory)
所以你可以从Python读取这些文件并提取你想要的数据 需要。
$ cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8190 1 00000000 300 0 0 2 -1
1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 6458 1 00000000 300 0 0 2 -1
2: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10425 1 00000000 300 0 0 2 -1
3: 8D0BA8C0:8801 689255D1:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 1680975 1 00000000 24 4 16 6 -1
4: 8D0BA8C0:D142 97E67D4A:01BB 06 00000000:00000000 03:000012E8 00000000 0 0 0 3 00000000
5: 8D0BA8C0:D1A1 96E67D4A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 1672130 1 00000000 24 4 18 5 -1
6: 8D0BA8C0:D148 97E67D4A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 1679875 1 00000000 24 4 20 5 -1
监听套接字的远程地址为 00000000:0000
地址:端口对为十六进制。看: * 如何将每个 /proc/net/tcp 条目与每个打开的套接字匹配?
您可以与/proc//fd 交叉引用。例如,sshd 是 在我的笔记本电脑上运行。
$ cat /var/run/sshd.pid
522
$ sudo ls -l /proc/522/fd
total 0
lrwx------ 1 root root 64 2011-09-15 21:32 0 -> /dev/null
lrwx------ 1 root root 64 2011-09-15 21:32 1 -> /dev/null
lrwx------ 1 root root 64 2011-09-15 21:32 2 -> /dev/null
lrwx------ 1 root root 64 2011-09-15 21:32 3 -> socket:[6456]
lrwx------ 1 root root 64 2011-09-15 21:32 4 -> socket:[6458]
Socket 6456对应第二行列出的inode 6458 /proc/net/tcp。
所以你可以从 proc 获取所有这些信息,但你最终可能会 重新发明 netstat -lntp
import psutil
connections = psutil.net_connections()
您可以尝试连接到有问题的端口,或模拟
netstat
。
执行后者将取决于操作系统。在 Linux 上,您可以检查
/proc/net/tcp
。看起来像这样:
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:C809 00000000:0000 0A 00000000:00000000 00:00000000 00000000 117 0 8381 1 ffff8802f22a8000 300 0 0 2 -1
1: 00000000:16CC 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1026 0 14336 1 ffff8802f2249440 300 0 0 2 -1
2: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 7876 1 ffff8802f2248000 300 0 0 2 -1
3: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8163 1 ffff8802f3578000 300 0 0 2 -1
4: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 981582 1 ffff8800d7a53600 300 0 0 2 -1
5: 00000000:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 9129 1 ffff8802edc886c0 300 0 0 2 -1
6: 00000000:021A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 9016 1 ffff8802edc88000 300 0 0 2 -1
7: 00000000:2B1C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1026 0 783709 1 ffff8803119cca40 300 0 0 2 -1
8: 00000000:977C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 24292 1 ffff8802f224e540 300 0 0 2 -1
您正在查找
0A
(“状态”)列中具有 st
的行。 local_address
中冒号后面的数字 - C809
、16CC
等 - 是有侦听进程的 TCP 端口号(十六进制)。
我知道我晚了几年,但现有的答案都不够好。
我在谷歌上寻找一个好的、优雅的解决方案来解决完全相同的问题,但已经发布的答案似乎都不够好,相反,我找到了自己的解决方案,我想将它们发布在这里,以帮助未来被重定向到这里的读者通过谷歌。
大多数操作系统都有一个名为
netstat
的可执行文件,可用于捕获侦听端口,在本例中我使用的是 Windows 10 和 Python 3.9.6 x64,但这是用 Python 编写的,因此您可以轻松地根据自己的需要进行调整使用案例。
使用普通
netstat
会非常慢,因为要进行所有名称解析,使用 netstat -n
会呈指数级更快,因为它不会浪费时间解析名称。
在Python 3.9.6中,使用
subproces.run()
来运行操作系统调用,并使用capture_output=True
来捕获stdout,然后使用结果进程的.stdout
属性来获取输出,结果是二进制形式,使用.decode()
来获取字符串。
那么输出应该如下所示:
Active Connections
Proto Local Address Foreign Address State
TCP 10.70.0.6:1134 40.83.240.146:443 ESTABLISHED
TCP 10.70.0.6:1283 117.18.232.200:443 CLOSE_WAIT
TCP 10.70.0.6:1609 198.252.206.25:443 ESTABLISHED
TCP 10.70.0.6:1621 198.252.206.25:443 ESTABLISHED
TCP 10.70.0.6:1691 74.125.24.102:443 ESTABLISHED
TCP 10.70.0.6:1727 142.251.10.94:443 ESTABLISHED
TCP 10.70.0.6:1728 142.251.10.100:443 TIME_WAIT
TCP 10.70.0.6:1731 172.217.194.119:443 TIME_WAIT
TCP 10.70.0.6:1735 74.125.24.113:443 ESTABLISHED
TCP 10.70.0.6:1787 104.244.42.130:443 ESTABLISHED
TCP 10.70.0.6:1796 151.101.1.69:443 ESTABLISHED
TCP 10.70.0.6:1797 151.101.196.193:443 ESTABLISHED
TCP 10.70.0.6:1799 74.125.130.132:443 ESTABLISHED
TCP 10.70.0.6:1800 198.252.206.25:443 ESTABLISHED
TCP 10.70.0.6:1805 3.209.45.230:443 TIME_WAIT
TCP 10.70.0.6:1806 3.219.6.82:443 TIME_WAIT
TCP 10.70.0.6:1807 3.211.239.214:443 TIME_WAIT
TCP 10.70.0.6:1816 140.82.113.26:443 ESTABLISHED
TCP 127.0.0.1:1053 127.0.0.1:1055 ESTABLISHED
TCP 127.0.0.1:1055 127.0.0.1:1053 ESTABLISHED
TCP 127.0.0.1:1057 127.0.0.1:1058 ESTABLISHED
TCP 127.0.0.1:1058 127.0.0.1:1057 ESTABLISHED
TCP 127.0.0.1:1061 127.0.0.1:1062 ESTABLISHED
TCP 127.0.0.1:1062 127.0.0.1:1061 ESTABLISHED
TCP 127.0.0.1:1763 127.0.0.1:1764 ESTABLISHED
TCP 127.0.0.1:1764 127.0.0.1:1763 ESTABLISHED
TCP 127.0.0.1:1766 127.0.0.1:1767 ESTABLISHED
TCP 127.0.0.1:1767 127.0.0.1:1766 ESTABLISHED
TCP 127.0.0.1:1810 127.0.0.1:2015 ESTABLISHED
TCP 127.0.0.1:1811 127.0.0.1:2015 ESTABLISHED
TCP 127.0.0.1:1820 127.0.0.1:1821 ESTABLISHED
TCP 127.0.0.1:1821 127.0.0.1:1820 ESTABLISHED
TCP 127.0.0.1:1829 127.0.0.1:9614 SYN_SENT
TCP 127.0.0.1:2015 127.0.0.1:1810 ESTABLISHED
TCP 127.0.0.1:2015 127.0.0.1:1811 ESTABLISHED
TCP 127.0.0.1:14845 127.0.0.1:14846 ESTABLISHED
TCP 127.0.0.1:14846 127.0.0.1:14845 ESTABLISHED
TCP 127.0.0.1:15004 127.0.0.1:15005 ESTABLISHED
TCP 127.0.0.1:15005 127.0.0.1:15004 ESTABLISHED
TCP 127.0.0.1:15013 127.0.0.1:15014 ESTABLISHED
TCP 127.0.0.1:15014 127.0.0.1:15013 ESTABLISHED
TCP 127.0.0.1:16976 127.0.0.1:16977 ESTABLISHED
TCP 127.0.0.1:16977 127.0.0.1:16976 ESTABLISHED
TCP 127.0.0.1:19278 127.0.0.1:19279 ESTABLISHED
TCP 127.0.0.1:19279 127.0.0.1:19278 ESTABLISHED
TCP 127.0.0.1:19280 127.0.0.1:19281 ESTABLISHED
TCP 127.0.0.1:19281 127.0.0.1:19280 ESTABLISHED
TCP 127.0.0.1:20695 127.0.0.1:21385 ESTABLISHED
TCP 127.0.0.1:21385 127.0.0.1:20695 ESTABLISHED
TCP 127.0.0.1:23460 127.0.0.1:23461 ESTABLISHED
TCP 127.0.0.1:23461 127.0.0.1:23460 ESTABLISHED
TCP 127.0.0.1:23462 127.0.0.1:23463 ESTABLISHED
TCP 127.0.0.1:23463 127.0.0.1:23462 ESTABLISHED
TCP 127.0.0.1:28343 127.0.0.1:28344 ESTABLISHED
TCP 127.0.0.1:28344 127.0.0.1:28343 ESTABLISHED
TCP 127.0.0.1:30307 127.0.0.1:30308 ESTABLISHED
TCP 127.0.0.1:30308 127.0.0.1:30307 ESTABLISHED
TCP 127.0.0.1:30311 127.0.0.1:30312 ESTABLISHED
TCP 127.0.0.1:30312 127.0.0.1:30311 ESTABLISHED
TCP 127.0.0.1:30313 127.0.0.1:30314 ESTABLISHED
TCP 127.0.0.1:30314 127.0.0.1:30313 ESTABLISHED
TCP 127.0.0.1:30316 127.0.0.1:30317 ESTABLISHED
TCP 127.0.0.1:30317 127.0.0.1:30316 ESTABLISHED
TCP 127.0.0.1:30319 127.0.0.1:30320 ESTABLISHED
TCP 127.0.0.1:30320 127.0.0.1:30319 ESTABLISHED
TCP 127.0.0.1:30584 127.0.0.1:30585 ESTABLISHED
TCP 127.0.0.1:30585 127.0.0.1:30584 ESTABLISHED
TCP 127.0.0.1:30623 127.0.0.1:30624 ESTABLISHED
TCP 127.0.0.1:30624 127.0.0.1:30623 ESTABLISHED
TCP 127.0.0.1:49669 127.0.0.1:49670 ESTABLISHED
TCP 127.0.0.1:49670 127.0.0.1:49669 ESTABLISHED
TCP 127.0.0.1:49690 127.0.0.1:49692 ESTABLISHED
TCP 127.0.0.1:49692 127.0.0.1:49690 ESTABLISHED
TCP [::1]:3306 [::1]:23468 ESTABLISHED
TCP [::1]:3306 [::1]:23469 ESTABLISHED
TCP [::1]:23468 [::1]:3306 ESTABLISHED
TCP [::1]:23469 [::1]:3306 ESTABLISHED
使用
splitlines()
来获取单独的行,然后使用列表切片来获取实际表的内容,这里我们使用索引4,然后使用正则表达式分割来分割空白字符,然后使用索引来获取本地地址值,然后最后使用冒号上的字符串分割和索引来获取端口。
代码:
import psutil
import re
import subprocess
def get_active_ports():
process = subprocess.run(['netstat', '-n'], capture_output=True)
report = process.stdout.decode().splitlines()
ports = set()
for i in report[4:]:
ports.add(re.split(':(?!.*:)', re.split('\s+', i)[2])[1])
return ports
或者单行:
set([re.split(':(?!.*:)', re.split('\s+', i)[2])[1] for i in subprocess.run(['netstat', '-n'], capture_output=True).stdout.decode().splitlines()[4:]])
性能:
In [119]: %timeit set([re.split(':(?!.*:)', re.split('\s+', i)[2])[1] for i in subprocess.run(['netstat', '-n'], capture_output=True).stdout.decode().splitlines()[4:]])
11.4 ms ± 315 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
或者,
psutil
有一个.net_connections()
方法,您可以从中获取端口。
只需使用集合理解来获取输出:
set(i.laddr.port for i in psutil.net_connections())
这种方法比前一种方法快得多:
In [103]: %timeit set(i.laddr.port for i in psutil.net_connections())
893 µs ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)