用Ruby写一个"合格"的端口扫描器:基于事件驱动的编程模型
Posted 每日一道算法题
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Ruby写一个"合格"的端口扫描器:基于事件驱动的编程模型相关的知识,希望对你有一定的参考价值。
上一篇文章“用Ruby写一个"合格"的端口扫描器(一)”中,我们介绍了如何使用多线程的进行端口扫描。现在,我们来了解一下,如何通过“事件驱动”的方式来编写端口扫描的代码。
在写代码之前,我们首先构思一下我们这个“事件驱动”的程序应该包括哪些方面:
第一,不能有任何的阻塞调用;
第二,需要一个“监视器”,监控所有的socket。
编写非阻塞代码
编写“事件驱动”的程序有一个关键的点:不能编写block的代码。但是socket的connect是一个同步调用,它会block住整个程序(在第一篇文章中,我们使用的是多线程解决的整个问题)。除了多线程的方式,怎么才能建立一个不block的socket呢?
幸好Ruby提供一个异步connect方法: connect_nonblock。这个方法接受一个remote_address 参数,如果连接成功,会返回0;如果连接失败,则会抛出异常。
监控socket
编写一个监控器的最简单的办法是将所有需要监控的对象放到一个数组中;然后写一个死循环,如果发现数组中的如何对象的状态变化了,就将其取出;直到数组为空,终止循环。所以,关键在于如何的监控对象的状态变化。
Ruby的IO中提供一个方法select,可以监控socket的状态变化。它接受四个参数:第一个是希望从中进行读取的IO对象数组;第二个是希望从中进行写入的IO对象数组;第三个是异常条件下使用的IO对象数组;第四个是超时的时间,单位是秒。实际上,IO.select底层调用的是select(2)。select(2)不是一个很好的实现方案,至少在NodeJS中并没有使用它。它有一个致命的问题在于,最多同时监控1024个IO对象。不过在这里使用还是绰绰有余了。
代码讲解
首先, 我定义了三个常量。
1. PORT_RANGE: 想扫描的端口区间,这里使用了Ruby比较有意思的语法:区间。在此处表示 1 到 1000 的数组
2. HOST: 接受一个环境变量,这样在运行程序的时候,我们可以指定需要扫描的主机。ENV是Ruby包装的进程环境,类似于Hash结构
3. TIME_TO_WAIT: 超时时间
1. 接着我创建了1000个TCP的IPV4的Socket
2. 在上面的代码中,我调用了socket的connect_nonblock方法,并将所有的socket保存在sockets数组中。
我使用一个loop循环来让程序在sockets没有全部处理完之前,一直保持运行
使用 IO.select来监控sockets的状态变化, 注意 IO.select 是一个阻塞调用,当没有可写的socket的时候,它会一直阻塞着,直到超时停止。所以,当writable超时后,循环结束。代码23会结束loop循环
当得到可写的socket后,我们就将其基本信息打印出来,然后从sockets数组中删除(因为不需要在监控了)
为了保证程序在规定的时间内(5秒)结束,我们使用了变量 expiration 来规定结束的时间点
如下是我的微信订阅号,谢谢大家关注~
以上是关于用Ruby写一个"合格"的端口扫描器:基于事件驱动的编程模型的主要内容,如果未能解决你的问题,请参考以下文章
怎样用C语言程序编输入一个学生的成绩,若成绩大于等于60提示成绩合格,否则提示需要努力学习的程序?
excel中运用数组判断逻辑时,如何忽略空单元格却不忽略0.
Ruby on Rails - 地址已在使用中 - 为“0.0.0.0”端口 3000 (Errno::EADDRINUSE) 绑定 (2)