写在前面
Scapy是一个 Python程序,它允许用户发送、嗅探、分析和伪造网络包。这种能力允许构建能够探测、扫描或攻击网络的工具,另外Scapy库还是在制作爬虫中也是很强大的一个库。
至于这个标题呢,写了大篇之后才发现全在写回调函数了,并没有关于Scapy库的内容了,所以临时起意改成这样了,回调函数建议还是看一看理解一下,然后Scapy库的内容呢我这边不打算继续更新了,因为对我自己来说没有太大效益,感兴趣的话看看其他人的资料吧。
一些函数
sniff(count=0,store=1,offline=None,prn=None,filter="None",L2socket=None,timeout=None,opened_socket=None,stop_filter=None,iface=None)
参数 | 涵义 | 默认值 |
---|---|---|
count | 抓取报的数量,设置为0时则一直捕获 | 0 |
store | 保存抓取的数据包或者丢弃,1保存,0丢弃 | 1 |
offline | 从PCAP文件中读取数据包,而不进行嗅探,默认为None | None |
prn | 为每个数据包定义一个回调函数 | None |
filter | 过滤规则,可以在里面定义WinreShark的过滤语法 | None |
L2socket | 使用给定的L2socket | None |
timeout | 在给定的事件后停止嗅探,默认为None | None |
opened_socket | 对指定的对象使用.recv 进行读取 |
None |
stop_filter | 定义一个函数,决定在抓到指定的数据之后停止 | None |
iface | 指定抓包的网卡,不指定则代表所有网卡 | None |
session | 流处理器 | TCPSession |
回调函数:一个高层调用底层,底层再回过头来调用高层的过程。
BPF:柏克莱封包过滤器(
Berkeley Packet Filter
,缩写BPF
),是类Unix系统上数据链路层的一种原始接口,提供原始链路层封包的收发。
回调函数(callback)
概念
回调函数就是一个被作为参数传递的函数。在C语言中,回调函数只能使用函数指针实现,在C++、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。
回调函数的使用可以大大提升编程的效率,这使得它在现代编程中被非常多地使用。同时,有一些需求必须要使用回调函数来实现。
最著名的回调函数调用有C/C++标准库stdlib.h/cstdlib中的快速排序函数qsort和二分查找函数bsearch中都会要求的一个与strcmp类似的参数,用于设置数据的比较方法。
机制
- 定义一个回调函数;
- 提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
- 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
意义
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
回调函数在实际中有许多作用。假设有这样一种情况:我们要编写一个库,它提供了某些排序算法的实现(如冒泡排序、快速排序、shell排序、shake排序等等),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,能让库可用于多种数据类型(int、float、string),此时,可以使用函数指针,并进行回调。
回调可用于通知机制。例如,有时要在A程序中设置一个计时器,每到一定时间,A程序会得到相应的通知,但通知机制的实现者对A程序一无所知。那么,就需一个具有特定原型的函数指针进行回调,通知A程序事件已经发生。实际上,API使用一个回调函数SetTimer
来通知A程序。如果没有提供回调函数,它还会把一个消息发往程序的消息队列。
另一个使用回调机制的API函数是EnumWindow
,它枚举屏幕上所有的顶层窗口,每个窗口都可以通过它调用另一个程序提供的函数,并传递窗口的处理程序。例如:如果被调用者返回一个值,就继续进行迭代;否则,退出。EnumWindow
并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。
C语言的回调函数只能通过函数指针实现,在C++中则可以使用匿名函数(lambda)或仿函数(functor)作为回调函数。
Python中的回调函数
Python中并没有指针的说法,一般都是讲函数名,简单来说就是定义一个函数,然后将这个函数的函数名传递给另一个函数做参数,以这个参数命名的函数就是回调函数。
def my_callbcak(args):
print(type(args))
print(*args)
def caller(args, func):
func(args)
caller((1,2), my_callbcak)
# 结果:
# <class 'tuple'>
# 1 2
其中:my_callback是回调函数,因为它作为参数传递给了caller
异步调用
既然写到了调用函数,那就也把异步调用也记录一下吧,这个理解起来还是挺难的,参考了别人的一些想法和内容,那么这里开始进入正题吧。
异步和同步
首先先说一下异步和同步这部分。同步和异步关注的是消息通信机制,synchronous communication/ asynchronous communication。
- 同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。
- 异步,和同步相反 调用方不会理解得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用
这些概念说实话是不是看起来非常的难懂,那么我们先看一点其他的。借鉴一下这位老哥的一些内容——jiajiahebangbang,他是从进程通信的角度去理解的。
在《操作系统》中关于异步和同步的部分是这样解释的:
进程间的通信时通过 send() 和 receive() 两种基本操作完成的。具体如何实现这两种基础操作,存在着不同的设计。
消息的传递有可能是阻塞的或非阻塞的 – 也被称为同步或异步的:
阻塞式发送(blocking send). 发送方进程会被一直阻塞, 直到消息被接受方进程收到。
非阻塞式发送(nonblocking send)。 发送方进程调用 send() 后, 立即就可以其他操作。
阻塞式接收(blocking receive) 接收方调用 receive() 后一直阻塞, 直到消息到达可用。
非阻塞式接受(nonblocking receive) 接收方调用 receive() 函数后, 要么得到一个有效的结果, 要么得到一个空值, 即不会被阻塞。
上述不同类型的发送方式和不同类型的接收方式,可以自由组合。也就是说,阻塞和非阻塞可以理解为是发送方的行为,同步和异步是接收方的行为。
除此之外再看一个实际的例子——我们现在去在一条步行街上的银行办理业务,根据场景可以解释为以下多种情况:
- 拿到号后,自己是一直在等候区等待(阻塞),还是出去逛街,时不时回来确认一下(非阻塞)。
- 轮到我们办理业务的时候,是我们自己确认轮到自己了(同步),还是银行会通知我们去办理业务(异步)。
对我自己来说,我比较愿意这样去理解这个内容:放在程序里讲,一个程序好比是一个事情,一个线程好比是一个处理这件事情的人,以往的同步性的程序是一个人从头到尾针对这件事情的每一个步骤一步一步进行,而采用异步的程序则是进行到某一个步骤时,这个步骤的内容我可以交给其他人去做,而我可以继续进行我的步骤。
看一下下面这个程序,算是很符合我这种说法了。
def computer(a, b, func):
return func(a, b)
def max(a, b):
return [a, b][a < b]
def min(a, b):
return [a, b][a > b]
def sum(a, b):
return str(int(a) + int(b))
if __name__ == "__main__":
a = input("请输入整数a:")
b = input("请输入整数b:")
res = computer(a, b, max)
print("Max of " + a + " and " + b + " is " + res)
res = computer(a, b, min)
print("Min of " + a + " and " + b + " is " + res)
res = computer(a, b, sum)
print("Sum of " + a + " and " + b + " is " + res)
在我的程序进行到求最大值的时候我可以将这个步骤交由max()
这个函数去做,而我的"main"部分可以继续执行,进行下面的求取最小值的计算,同样的我这部分可以去交由min()
这个函数去做,这就是异步。有的同学可能就要说了,这个程序就是一个线性的同步程序啊!这个问题呢,主要是并没有进行线程部分的编写,写了线程之后效果就会更加明显。如果你觉得这么讲的话还是有纰漏呢,就请继续往下看,看完下面你就会理解了。
异步的原理
如果使用一个异步调用方法跑Java概念上去了hhh的时候,可以理解为,发送完请求后,我们就可以继续去做自己的事情,然后在一个合适的节点去取数据即可。这句就是一个重点,在合适的节点去取数据,而前面你所感觉得纰漏就是我用一个单线程的程序去讲解这个概念,纰漏就出在这里。嗐!主要还是我目前位置还没有写过多线程的程序,所以手边并没有更好的例子去说明这个。
除此之外,在使用异步调用的时候需要明确,是谁帮我们把这些事情做完的。这点一般分为两个情况:
第一种情况,本地IO操作时,可以通过DMA功能实现,在调用DMA传输数据的时候,CPU是不需要执行处理的,只需要发起传输和等待传输即可,也就是说,在这段时间里,CPU可以干点别的事情。DMA的英文拼写是“Direct Memory Access”,汉语的意思就是直接内存访问。DMA既可以指内存和外设直接存取数据这种内存访问的计算机技术,又可以指实现该技术的硬件模块(对于通用计算机PC而言,DMA控制逻辑由CPU和DMA控制接口逻辑芯片共同组成,嵌入式系统的DMA控制器内建在处理器芯片内部,一般称为DMA控制器,DMAC)。
第二种情况,通过多线程实现,主线程发起请求操作(这里用线程解释,多进程也是可以的),系统选取一个线程接过这个主线程的请求任务,然后当异步调用晚餐后,系统会从可用线程中选取一个线程执行回调函数,将结果推回给主线程。这里的异步调用,主要是为了让调用方法的主线程不需要同步等待在这个函数调用上,从而可以让主线程继续执行它下面的代码。
第一种情况呢,涉及到物理底层,我这边就不过多涉及了,关于第二种情况,实现的核心思路在于:
-
其他线程/进程执行IO操作,让发起请求方可以不用等待。
-
在执行完异步调用后,通知调用者提取相关数据(这里可以使用注册回调函数的办法)。
临时结束
写到这里其实我意识到一个问题,写了这么大篇幅还都是在回调函数上,并没有很多关于Scapy库的使用,后面我查了些资料,感觉Scapy库这部分对于我自己来说并没有很大的实际的效益,所以呢,这篇就先写到这里了,回调函数还是可以看一看的。
Comments | NOTHING