项目需求
代码检测某个端口是否被占用,从而找出多个连续空闲的端口,由于需要 Windows、Linux 都能运行,所以选择了 Python,而不是 shell。
实现方案
Python 的 socket 模块
思路是 try 尝试连接某个端口,如果能连接上,表示端口被占用,否则端口空闲。
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
中, 说明端口不被占用.
存在问题
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
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
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 这几个平台查看端口信息的方式不同,
先进行机器平台的判断,然后调用各个平台的端口占用判断函数
因为需求是找出连续的空闲端口,当中途只要有一个端口占用, 就跳出循环
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)
相关知识点总结
os.popen()
可以调用系统的一些shell命令
os.popen().readlines()
读取调用shell命令后的回显信息
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 表示 进程号 54849 进程名 nginx