实例探究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里对list中的整数求平均并排序
Sep 12 Python
Python记录详细调用堆栈日志的方法
May 05 Python
Python 多线程抓取图片效率对比
Feb 27 Python
python 3利用BeautifulSoup抓取div标签的方法示例
May 28 Python
使用PyCharm创建Django项目及基本配置详解
Oct 24 Python
Python2和Python3之间的str处理方式导致乱码的讲解
Jan 03 Python
详解Python中的各种转义符\n\r\t
Jul 10 Python
解决python明明pip安装成功却找不到包的问题
Aug 28 Python
使用Python的turtle模块画国旗
Sep 24 Python
pycharm sciview的图片另存为操作
Jun 01 Python
Python3基于print打印带颜色字符串
Jul 06 Python
python 绘制国旗的示例
Sep 27 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下对数组进行排序的函数
2010/08/08 PHP
通过5个php实例细致说明传值与传引用的区别
2012/08/08 PHP
实现PHP多线程异步请求的3种方法
2014/01/17 PHP
JavaScript asp.net 获取当前超链接中的文本
2009/04/14 Javascript
javascript开发技术大全-第3章 js数据类型
2011/07/03 Javascript
中国地区三级联动下拉菜单效果分析
2012/11/15 Javascript
javascript数组的使用
2013/03/28 Javascript
javascript创建和存储cookie示例
2014/01/07 Javascript
JS实现超精简响应鼠标显示二级菜单代码
2015/09/12 Javascript
JavaScript+CSS无限极分类效果完整实现方法
2015/12/22 Javascript
对象题目的一个坑 理解Javascript对象
2015/12/22 Javascript
微信小程序 用户数据解密详细介绍
2017/01/09 Javascript
Bootstrap中data-target 到底是什么
2017/02/14 Javascript
使用jQuery卸载全部事件的思路详解
2017/04/03 jQuery
JQuery和html+css实现带小圆点和左右按钮的轮播图实例
2017/07/22 jQuery
js实现简易聊天对话框
2017/08/17 Javascript
JS遍历JSON数组及获取JSON数组长度操作示例【测试可用】
2018/12/12 Javascript
jQuery实现的卷帘门滑入滑出效果【案例】
2019/02/18 jQuery
JS面向对象之单选框实现
2020/01/17 Javascript
react 生命周期实例分析
2020/05/18 Javascript
绘制微信小程序验证码功能的实例代码
2021/01/05 Javascript
three.js 实现露珠滴落动画效果的示例代码
2021/03/01 Javascript
[54:51]Ti4 冒泡赛第二轮LGD vs C9 3
2014/07/14 DOTA
让python在hadoop上跑起来
2016/01/27 Python
Django中redis的使用方法(包括安装、配置、启动)
2018/02/21 Python
浅谈Python中重载isinstance继承关系的问题
2018/05/04 Python
python实现网页自动签到功能
2019/01/21 Python
浅谈Python 命令行参数argparse写入图片路径操作
2020/07/12 Python
美国当红的名品折扣网:Gilt Groupe
2016/08/15 全球购物
介绍一下Cookie和Session及他们之间的区别
2012/11/20 面试题
如何查找和删除数据库中的重复数据
2014/11/05 面试题
即兴演讲稿
2014/01/04 职场文书
中药学专业毕业生推荐信
2014/07/10 职场文书
暑期学习心得体会
2014/09/02 职场文书
2015年秘书个人工作总结
2015/04/25 职场文书
小学一年级班主任工作经验交流材料
2015/11/02 职场文书