【发布时间】:2016-04-12 19:18:08
【问题描述】:
下面的完整(非工作)代码
此处的完整(工作,无线程)代码:http://pastebin.com/KUYzNtT2
我编写了一个小脚本,它执行以下操作:
- 从数据库中提取网络信息
- 在一个 cidr 中对每个 IP 进行 Ping(即 - 192.168.0.0/24);如果已经启动,请测试某个端口是否打开
- 显示结果
这工作正常,但我想实现线程以使脚本运行得更快;因为我有数千个 IP 需要扫描,而且需要很长时间。
我玩过线程教程,但似乎无法掌握如何在我的脚本中实现它。
感谢任何想法或建议。
编辑:根据本指南,我朝不同的方向前进:http://chriskiehl.com/article/parallelism-in-one-line/
现在我运行程序并得到:File "port_test.py", line 39, in display_results
for (client, location, cidr) in results:
ValueError: too many values to unpack (expected 3)
我不明白为什么。想法?
**编辑:我想我知道为什么它失败了,看起来 pool.map 只需要一个数据点。如果我只查询数据库的 CIDR 而不是其他两列,则程序开始吐出数据(比以前快得多)。所以现在我需要弄清楚如何将其他两列添加到结果中,然后对结果进行排序以使其有意义(结果没有顺序,我认为这是有道理的)
#! /usr/bin/python
# Import modules
import socket
import subprocess
import ipaddress
import mysql.connector
import configparser
import logging
import coloredlogs
from multiprocessing.dummy import Pool as ThreadPool
#logging.basicConfig(format='%(levelname)s:%(message)s',level=logging.INFO)
coloredlogs.install(level='DEBUG')
coloredlogs.DEFAULT_LOG_FORMAT = '%(asctime)s -- %(message)s'
# read from the config file
config = configparser.ConfigParser()
config.read('config.ini')
db=config['mysql']
net=config['network']
port = int(net['port'])
# create the connection, connect, and setup the query
cnx = mysql.connector.connect(user=db['user'], database=db['database'], password=db['password'])
cursor = cnx.cursor()
query = ("select fw.net_cidr as cidr "
"from firewalls fw "
"left join clients c on c.id = fw.client_id "
"left join locations l on l.id = fw.location_id "
"where fw.net_cidr <> '' and c.active = '1' and fw.active = '1'")
cursor.execute(query)
results = cursor.fetchall()
def display_results(results):
# execute and display the results
for (cidr) in results:
logging.info("{} --> ".format(cidr))
try:
# Prompt the user to input a network address
net_addr = str(cidr)
# Create the network
ip_net = ipaddress.ip_network(net_addr)
# Get all hosts on that network
all_hosts = list(ip_net.hosts())
except ValueError as e:
logging.warning(e)
continue
# For each IP address in the subnet, test to see if port 3389 is open
for i in range(len(all_hosts)):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(.25)
result = sock.connect_ex((str(all_hosts[i]),port))
if result == 0:
logging.info(str(all_hosts[i]) + ": " + net['port'] + " is open")
else:
logging.debug(str(all_hosts[i]) + ": " + net['port'] + " is not open")
# make a pool of workers
pool = ThreadPool(4)
# ping the cidrs in their own thread
pool.map(display_results, results)
pool.close()
pool.join()
# close the database connection
cursor.close()
cnx.close()
【问题讨论】:
-
见this answer。您需要您的线程使用不同的数据(它们不能将游标共享给单个数据库查询)来完成不同的工作。您有四个线程调用一个函数,该函数需要一个没有参数的参数然后您在尝试启动线程后使用游标的结果,然后关闭连接而不等待查看线程是否完成。 (但他们在这一点上是因为他们未能使用错误数量的参数调用函数)
-
另外,看看同时具有 ProcessPoolExecutor 和 ThreadPoolExecutor 的 concurrent.futures 模块。他们在文档中有很好的例子。蟒蛇3:docs.python.org/3/library/concurrent.futures.html蟒蛇2.7:pythonhosted.org/futures
-
请记住,由于全局解释器锁,在 CPython 中一次只能有一个线程执行 Python 字节码。您在线程方面的收获可能没有您预期的那么大。尝试例如
multiprocessing.Pool代替。 -
@jmd9qs “使用不同的数据”我的意思是你应该在线程之间拆分结果。你可以通过将所有数据加载到一个列表中来做到这一点,如果它对于可用内存来说不会太大的话。您可以通过打开与数据库的单独连接并使用 LIMIT 和 OFFSET 约束运行查询来做到这一点。或者您可以通过创建一个线程安全队列(我会考虑编写一个生成器函数)来实现,该队列一次返回一个项目并让线程从队列中获取数据。
-
@jmd9qs 现在您的代码执行此操作:对数据库中的每一行执行以下四次:扫描整个网络并打印结果,然后创建一个线程不执行任何操作。您的代码完全符合我链接到的问题,即在创建线程之前调用函数,这完全违背了线程的目的和概念。此外,在关闭数据库连接并退出之前,它仍然不会等待线程完成。
标签: python multithreading sockets