用python编写nmap扫描工具--采用协程的方式

更新时间:2021-07-26 13:53:57点击次数:587次
协程是一种轻量级的线程,协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。也就是说同一线程下的一段代码执行着执行着就可以中断,然后跳去执行另一段代码,当再次回来执行代码块的时候,接着从之前中断的地方开始执行。   
协程的优点:
1、执行效率高,尤其是在线程数较多的情况下,与多线程对比的优势更明显
2、不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好,因为执行效率比多线程高很多。
缺点:
1、无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
2、进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
接下来,让我们通过一段代码来看一下运行的效果:
import gevent
from gevent import monkey
import time 
def fun1():
    for num in range(3):
        print('fun1方法正在运行')
        #time.sleep(1) 
def fun2():
    for num in range(3):
        print('fun2方法正在运行')
        #time.sleep(1)
# 创建协程对象
t1 = gevent.spawn(fun1)
t2 = gevent.spawn(fun2)
monkey.patch_all()
gevent.joinall([t1, t2])
以上代码执行的时候,输出结果如下:
fun1方法正在运行
fun1方法正在运行
fun2方法正在运行
fun2方法正在运行
fun2方法正在运行
是不是跟预想的不一样呢,是的,因为程序执行没有阻塞/中断,所以打印结果没有交叉打印 ,把time.sleep(1)放开后,再执行:
import gevent
from gevent import monkey
import time
 def fun1():
    for num in range(3):
        print('fun1方法正在运行')
        time.sleep(1)  # 协程遇到耗时操作后会自动切换其他协程运行
  def fun2():
    for num in range(3):
        print('fun2方法正在运行')
        time.sleep(1)  # 协程遇到耗时操作后会自动切换其他协程运行 
# 创建协程对象
t1 = gevent.spawn(fun1)
t2 = gevent.spawn(fun2)
monkey.patch_all()
gevent.joinall([t1, t2])
打印结果如下:
fun1方法正在运行
fun2方法正在运行
fun1方法正在运行
fun2方法正在运行
fun1方法正在运行
fun2方法正在运行
代码说明:
本次采用gevent库实现协程的相关操作,在使用之前需要先安装该插件。
安装命令:pip install gevent
gevent.spawn()函数:创建协程对象
gevnet.joinall([传入携程对象列表]):会等待所有协程对象运行结束后再退出
接下来改造端口扫描的代码,采用协程的方式实现:
import socket
import time
import gevent
from gevent import monkey
from gevent.pool import Pool
monkey.patch_all()
def scan_port(host, port):
    sk = socket.socket()
    sk.settimeout(0.5)
    conn_result = sk.connect_ex((host, port))
    if conn_result == 0:
        print(f'服务器{host}的{port}端口已开放')
    sk.close() 
def gevent_scan_host(host):
    # 8.129.162.225
    start_time = time.time()
    run_list = []
    g = Pool(200)   # 限制协程并发数量,单线程的,不要设置太大
    for port in range(0, 65536):
        run_list.append(g.spawn(scan_port, host, port))
    gevent.joinall(run_list)
    end_time = time.time()
    print(f'耗时:{end_time-start_time}')
host = input('请输入服务器ip地址:')
gevent_scan_host(host)

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息