实例探究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自动安装pip
Apr 24 Python
python使用pyhook监控键盘并实现切换歌曲的功能
Jul 18 Python
解析Python中的二进制位运算符
May 13 Python
在Python的Django框架中编写编译函数
Jul 20 Python
TensorFlow实现非线性支持向量机的实现方法
Apr 28 Python
使用PyInstaller将python转成可执行文件exe笔记
May 26 Python
Python实现socket非阻塞通讯功能示例
Nov 06 Python
Python 将json序列化后的字符串转换成字典(推荐)
Jan 06 Python
使用 Python 遍历目录树的方法
Feb 29 Python
django实现模型字段动态choice的操作
Apr 01 Python
Tensorflow之MNIST CNN实现并保存、加载模型
Jun 17 Python
Django与AJAX实现网页动态数据显示的示例代码
Feb 24 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
PHP简单的MVC框架实现方法
2015/12/01 PHP
PHP查询分页的实现代码
2017/06/09 PHP
PHP实现的登录页面信息提示功能示例
2017/07/24 PHP
基于PHP实现邮箱验证激活过程详解
2020/10/28 PHP
jquery easyui combobox模糊过滤(示例代码)
2013/11/30 Javascript
jQuery实现单击按钮遮罩弹出对话框(仿天猫的删除对话框)
2014/04/10 Javascript
JS实现很酷的EMAIL地址添加功能实例
2015/02/28 Javascript
jQuery实现根据类型自动显示和隐藏表单
2015/03/18 Javascript
javascript实现点击按钮弹出一个可关闭层窗口同时网页背景变灰的方法
2015/05/13 Javascript
javascript判断网页是关闭还是刷新
2015/09/12 Javascript
JavaScript类型系统之基本数据类型与包装类型
2016/01/06 Javascript
JavaScript学习笔记之ES6数组方法
2016/03/25 Javascript
jQuery实现遮罩层登录对话框
2016/12/29 Javascript
jQuery编写设置和获取颜色的插件
2017/01/09 Javascript
js模态对话框使用方法详解
2017/02/16 Javascript
jQuery实现简单漂亮的Nav导航菜单效果
2017/03/29 jQuery
nodejs之get/post请求的几种方式小结
2017/07/26 NodeJs
使用JS实现气泡跟随鼠标移动的动画效果
2017/09/16 Javascript
[01:04:14]VP vs TNC 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
Python应用03 使用PyQT制作视频播放器实例
2016/12/07 Python
python+matplotlib实现动态绘制图片实例代码(交互式绘图)
2018/01/20 Python
Django中的Model操作表的实现
2018/07/24 Python
OpenCV HSV颜色识别及HSV基本颜色分量范围
2019/03/22 Python
python使用多线程查询数据库的实现示例
2020/08/17 Python
香港演唱会订票网站:StubHub香港
2019/10/10 全球购物
泰国最新活动和优惠:Megatix
2020/05/07 全球购物
如何向接受结构参数的函数传入常数值
2016/02/17 面试题
英语文学专业学生的自我评价
2013/10/31 职场文书
酒吧副总经理岗位职责
2013/12/10 职场文书
学生党支部先进事迹
2014/02/04 职场文书
《藤野先生》教学反思
2014/02/19 职场文书
cf搞笑广告词
2014/03/14 职场文书
蛋糕店创业计划书范文
2014/09/21 职场文书
项目合作意向书
2015/05/08 职场文书
消防安全主题班会
2015/08/12 职场文书
Java反应式框架Reactor中的Mono和Flux
2021/07/25 Java/Android