项目需求
代码检测某个端口是否被占用,从而找出多个连续空闲的端口,由于需要 Windows、Linux 都能运行,所以选择了 Python,而不是 shell。
实现方案
Python 的 socket 模块
思路是 try 尝试连接某个端口,如果能连接上,表示端口被占用,否则端口空闲。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def isInuse(ipList, port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) flag=True for ip in ipList: try: s.connect((ip, int(port))) s.shutdown(2) print '%d is inuse' % port flag=True break except: print '%d is free' % port flag=False return flag
|
在try
模块中 如果 s.connect((ip, int(port)))
如果能成功, 说明端口被占用.
否则, connect
不成功, 会进到except
中, 说明端口不被占用.
存在问题
1 2 3 4
| netstat -tnpl Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 11563/java tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN 11563/java
|
源 ip 、源端口 + 目的 ip、目的端口 可以确定一条 tcp 连接。
本来以为 s.connect((ip, int(port)))
,只需要检测 "127.0.0.1","0.0.0.0"
后来发现还有可能是局域网ip 如 10.170.70.87
等等
可以通过这个方法根据远程 hostname 获得其 ip
1 2 3
| def getLocalIp(): localIP = socket.gethostbyname(socket.gethostname()) return localIP
|
代码
从某个端口 port 开始,进行检测,port + 1、port + 2 … ,如果连续的空闲端口数目达到需求的 N,则 port ~ port + N 为需求的端口。
如果中途某个 port + L 端口被占用,那么从新的端口 port + L + 1 开始寻找。
本代码只针对 ipList = ("127.0.0.1","0.0.0.0",getLocalIp())
这3个 ip 进行 connect
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
| import sys import os import socket def isInuse(ipList, port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) flag=True for ip in ipList: try: s.connect((ip, int(port))) s.shutdown(2) print '%d is inuse' % port flag=True break except: print '%d is free' % port flag=False return flag def getLocalIp(): localIP = socket.gethostbyname(socket.gethostname()) return localIP def checkNinePort(startPort): flag = True ipList = ("127.0.0.1","0.0.0.0",getLocalIp()) for i in range(1, 10): if (isInuse(ipList, startPort)): flag = False break else: startPort = startPort + 1 return flag, startPort def findPort(startPort): while True: flag, endPort = checkNinePort(startPort) if (flag == True): #ninePort is ok break else: startPort = endPort + 1 return startPort def main(): startPort=51988 # startPort = int(sys.argv[1]) print findPort(startPort) main()
|
字符串匹配
第一种方法的准确性依赖于 connect((ip, int(port)))
中的 ip,
需要找到完备的 ip 集合的, 才可以确定这个端口不被占用,难度较大
思路
**在 linux 用 netstat -tnpl
可以得到端口监听信息,
观察 tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 11563/java
出现了 0 0.0.0.0:8080
所以 8080端口是被占用的
对这些输出的字符信息进行搜索 :8080
, 如果存在, 就表示 8080 端口是LISTEN.
如果输出结果中不存在 :8080
的相关字符,表示这个端口不被占用.
代码
WINDOWS、 LINUX 这几个平台查看端口信息的方式不同,
先进行机器平台的判断,然后调用各个平台的端口占用判断函数
因为需求是找出连续的空闲端口,当中途只要有一个端口占用, 就跳出循环
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| import os import platform import sys def isInuseWindow(port): if os.popen('netstat -an | findstr :' + str(port)).readlines(): portIsUse = True print '%d is inuse' % port else: portIsUse = False print '%d is free' % port return portIsUse def isInuseLinux(port): #lsof -i:8080 #not show pid to avoid complex if os.popen('netstat -na | grep :' + str(port)).readlines(): portIsUse = True print '%d is inuse' % port else: portIsUse = False print '%d is free' % port return portIsUse def choosePlatform(): #'Windows-7-6.1.7601-SP1' #'linux-2.6.32-431.23.3.el6.x86_64-x86_64-with-centos-6.5-final' machine = platform.platform().lower() if 'windows-' in machine: return isInuseWindow elif 'linux-' in machine: return isInuseLinux else: print 'Error, sorry, platform is unknown' exit(-1) def checkNinePort(startPort): isInuseFun = choosePlatform() nineIsFree = True for i in range(1, 10): if (isInuseFun(startPort)): nineIsFree = False break else: startPort = startPort + 1 return nineIsFree, startPort def findPort(startPort): while True: flag, endPort = checkNinePort(startPort) if (flag == True): # ninePort is ok break else: startPort = endPort + 1 return startPort def main(startPort): firstPort=findPort(startPort) print 'First port of nine free ports is ', firstPort if __name__ == '__main__' : if len(sys.argv) > 1: print len(sys.argv) startPort = int(sys.argv[1]) else: startPort = 500 main(startPort)
|
相关知识点总结
可以调用系统的一些shell命令
读取调用shell命令后的回显信息
1 2 3 4 5 6 7 8 9 10 11
| netstat -tnpl -tnpl 各个参数的含义 -l或--listening 显示监控中的服务器的Socket。 -n或--numeric 直接使用IP地址,而不通过域名服务器。 -p或--programs 显示正在使用Socket的程序识别码和程序名称。 -t或--tcp 显示TCP传输协议的连线状况 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 9890/nginx 最后的9890/nginx 表示 进程号 9890 进程名 nginx
|