实例探究Python以并发方式编写高性能端口扫描器的方法


Posted in Python onJune 14, 2016

关于端口扫描器
端口扫描工具(Port Scanner)指用于探测服务器或主机开放端口情况的工具。常被计算机管理员用于确认安全策略,同时被攻击者用于识别目标主机上的可运作的网络服务。

端口扫描定义是客户端向一定范围的服务器端口发送对应请求,以此确认可使用的端口。虽然其本身并不是恶意的网络活动,但也是网络攻击者探测目标主机服务,以利用该服务的已知漏洞的重要手段。端口扫描的主要用途仍然只是确认远程机器某个服务的可用性。

扫描多个主机以获取特定的某个端口被称为端口清扫(Portsweep),以此获取特定的服务。例如,基于SQL服务的计算机蠕虫就会清扫大量主机的同一端口以在 1433 端口上建立TCP连接。

Python实现

端口扫描器原理很简单,无非就是操作socket,能connect就认定这个端口开放着。

import socket 
def scan(port): 
  s = socket.socket() 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
if __name__ == '__main__': 
  map(scan,range(1,65536))

这样一个最简单的端口扫描器出来了。
等等喂,半天都没反应,那是因为socket是阻塞的,每次连接要等很久才超时。
我们自己给它加上的超时。

s.settimeout(0.1)

再跑一遍,感觉快多了。

多线程版本

import socket 
import threading 
def scan(port): 
  s = socket.socket() 
  s.settimeout(0.1) 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
 
if __name__ == '__main__': 
  threads = [threading.Thread(target=scan, args=(i,)) for i in xrange(1,65536)] 
  map(lambda x:x.start(),threads)

运行一下,哇,好快,快到抛出错误了。thread.error: can't start new thread。
想一下,这个进程开启了65535个线程,有两种可能,一种是超过最大线程数了,一种是超过最大socket句柄数了。在linux可以通过ulimit来修改。
如果不修改最大限制,怎么用多线程不报错呢?
加个queue,变成生产者-消费者模式,开固定线程。

多线程+队列版本

import socket 
import threading 
from Queue import Queue 
def scan(port): 
  s = socket.socket() 
  s.settimeout(0.1) 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
 
def worker(): 
  while not q.empty(): 
    port = q.get() 
    try: 
      scan(port) 
    finally: 
      q.task_done() 
 
if __name__ == '__main__': 
  q = Queue() 
  map(q.put,xrange(1,65535)) 
  threads = [threading.Thread(target=worker) for i in xrange(500)] 
  map(lambda x:x.start(),threads) 
  q.join()

这里开500个线程,不停的从队列取任务来做。

multiprocessing+队列版本
总不能开65535个进程吧?还是用生产者消费者模式

import multiprocessing 
def scan(port): 
  s = socket.socket() 
  s.settimeout(0.1) 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
 
def worker(q): 
  while not q.empty(): 
    port = q.get() 
    try: 
      scan(port) 
    finally: 
      q.task_done() 
 
if __name__ == '__main__': 
  q = multiprocessing.JoinableQueue() 
  map(q.put,xrange(1,65535)) 
  jobs = [multiprocessing.Process(target=worker, args=(q,)) for i in xrange(100)] 
  map(lambda x:x.start(),jobs)

注意这里把队列作为一个参数传入到worker中去,因为是process safe的queue,不然会报错。
还有用的是JoinableQueue(),顾名思义就是可以join()的。

gevent的spawn版本

from gevent import monkey; monkey.patch_all(); 
import gevent 
import socket 
... 
if __name__ == '__main__': 
  threads = [gevent.spawn(scan, i) for i in xrange(1,65536)] 
  gevent.joinall(threads)

注意monkey patch必须在被patch的东西之前import,不然会Exception KeyError.比如不能先import threading,再monkey patch.

gevent的Pool版本

from gevent import monkey; monkey.patch_all(); 
import socket 
from gevent.pool import Pool 
... 
if __name__ == '__main__': 
  pool = Pool(500) 
  pool.map(scan,xrange(1,65536)) 
  pool.join()

concurrent.futures版本

import socket 
from Queue import Queue 
from concurrent.futures import ThreadPoolExecutor 
... 
if __name__ == '__main__': 
  q = Queue() 
  map(q.put,xrange(1,65536)) 
  with ThreadPoolExecutor(max_workers=500) as executor: 
    for i in range(500): 
      executor.submit(worker,q)
Python 相关文章推荐
python执行等待程序直到第二天零点的方法
Apr 23 Python
python生成随机图形验证码详解
Nov 08 Python
Python生成器以及应用实例解析
Feb 08 Python
使用TensorFlow实现二分类的方法示例
Feb 05 Python
Windows下pycharm创建Django 项目(虚拟环境)过程解析
Sep 16 Python
python装饰器代替set get方法实例
Dec 19 Python
Python GUI自动化实现绕过验证码登录
Jan 10 Python
Windows下Pycharm远程连接虚拟机中Centos下的Python环境(图文教程详解)
Mar 19 Python
pandas使用之宽表变窄表的实现
Apr 12 Python
将pymysql获取到的数据类型是tuple转化为pandas方式
May 15 Python
python 调用API接口 获取和解析 Json数据
Sep 28 Python
Django框架之路由用法
Jun 10 Python
Python使用dis模块把Python反编译为字节码的用法详解
Jun 14 #Python
Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
Jun 14 #Python
Python的Flask框架中使用Flask-SQLAlchemy管理数据库的教程
Jun 14 #Python
全面了解Python的getattr(),setattr(),delattr(),hasattr()
Jun 14 #Python
浅谈python中的getattr函数 hasattr函数
Jun 14 #Python
深入解析Python中的线程同步方法
Jun 14 #Python
详解Python中的Descriptor描述符类
Jun 14 #Python
You might like
smarty模板局部缓存方法使用示例
2014/06/17 PHP
PHP中soap的用法实例
2014/10/24 PHP
创建、调用JavaScript对象的方法集锦
2014/12/24 Javascript
DOM 事件流详解
2015/01/20 Javascript
javaScript的函数对象的声明详解
2015/02/06 Javascript
jquery中filter方法用法实例分析
2015/02/06 Javascript
JavaScript模拟重力状态下抛物运动的方法
2015/03/03 Javascript
jQuery siblings()用法实例详解
2016/04/26 Javascript
jquery 抽奖小程序实现代码
2016/10/12 Javascript
Bootstrop实现多级下拉菜单功能
2016/11/24 Javascript
JavaScript中的普通函数和箭头函数的区别和用法详解
2017/03/21 Javascript
详解如何将angular-ui的图片轮播组件封装成一个指令
2017/05/09 Javascript
在vue+element ui框架里实现lodash的debounce防抖
2019/11/13 Javascript
vue 使用插槽分发内容操作示例【单个插槽、具名插槽、作用域插槽】
2020/03/06 Javascript
Bootstrap实现前端登录页面带验证码功能完整示例
2020/03/26 Javascript
[05:26]TI10典藏宝瓶套装外观展示
2020/07/03 DOTA
总结Python编程中三条常用的技巧
2015/05/11 Python
Python开发之快速搭建自动回复微信公众号功能
2016/04/22 Python
解决python中使用plot画图,图不显示的问题
2018/07/04 Python
浅谈Python的list中的选取范围
2018/11/12 Python
python3 实现对图片进行局部切割的方法
2018/12/05 Python
一文了解Python并发编程的工程实现方法
2019/05/31 Python
Python使用matplotlib实现交换式图形显示功能示例
2019/09/06 Python
Python操作Excel工作簿的示例代码(\*.xlsx)
2020/03/23 Python
HTML5+CSS设置浮动却没有动反而在中间且错行的问题
2020/05/26 HTML / CSS
DJI大疆德国官方商城:大疆无人机
2018/09/01 全球购物
纯净、自信、100%的羊绒服装:360Cashmere
2021/02/20 全球购物
下面这个程序执行后会有什么错误或者效果
2014/11/03 面试题
单位婚育证明范本
2014/11/21 职场文书
投标售后服务承诺书
2015/04/29 职场文书
小时代观后感
2015/06/10 职场文书
十七岁的单车观后感
2015/06/12 职场文书
Python基础之字符串格式化详解
2021/04/21 Python
springboot使用Redis作缓存使用入门教程
2021/07/25 Redis
AJAX实现省市县三级联动效果
2021/10/16 Javascript
Python Numpy库的超详细教程
2022/04/06 Python