使用Python识别正在监听的端口

8
在翻译一些bash脚本时,我遇到了很多netstat -an的用法,以查找我们的服务是否在监听。虽然我知道可以使用subprocess.call或其他甚至popen来解决,但我更愿意使用pythonic的解决方案,这样我就不会利用我们所操作的unix环境。
据我所知,socket模块应该有一些东西,但我没有看到任何检查正在监听端口的内容。可能是我没有理解一个简单的技巧,但到目前为止我只知道如何连接到一个套接字,并编写一些让我知道连接失败的东西。但并没有发现具体检查端口是否在监听的方法。
有什么想法吗?
6个回答

11

要不试试使用以下代码连接数据库:

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()

我认为这不足够。它告诉你有“某事”正在监听,但它可能是任何东西,不一定是你期望的那个进程。 - richardw
1
Richardw,你能详细说明一下吗? - José Tomás Tocino

6

我知道这个问题很老,但是我写这篇文章是为了初学者。如果你想识别系统上的监听端口,可以使用下面的代码。

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.

4
在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对应于在/proc/net/tcp文件的第二行列出的inode 6458。

因此,您可以从proc获取所有这些信息,但您可能会重新发明netstat -lntp。


4

2
你可以尝试连接指定的端口,或者模拟 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                     

您需要查找在 st(“状态”)列中具有0A的行。冒号后面的数字在local_address中-- C80916CC等 -- 是TCP端口号(以十六进制表示),其中存在侦听进程。


1
我知道我晚了几年,但是现有的答案都不够好。我在谷歌上搜索了一个好的、优雅的解决方案,正好解决了同样的问题,但是已经发布的答案都不够好。相反,我找到了自己的解决方案,并想在这里发布它们,以帮助未来通过谷歌重定向到此处的读者。
大多数操作系统都有一个名为 netstat 的可执行文件,可以用来捕获监听端口,在本例中,我使用的是 Windows 10 和 Python 3.9.6 x64,但这是用 Python 编写的,所以您可以轻松地将其适应于自己的用例。
使用纯粹的 netstat 将非常慢,因为所有的名称解析都会浪费时间,使用 netstat -n 将指数级地更快,因为它不会浪费时间来解析名称。
在 Python 3.9.6 中,使用 subprocess.run() 运行 os 调用,并使用 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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接