美文网首页
wifijammer源码分析二

wifijammer源码分析二

作者: lifesmily | 来源:发表于2017-05-03 21:06 被阅读38次

    主要详细学习代码中以下几个模块

    from signal import SIGINT, signal
    import argparse
    import socket
    import struct
    import fcntl
    

    signal模块

    信号(signal)-- 进程之间通讯的方式,是一种软件中断。一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号。
    几个常用信号:
    SIGINT 终止进程 中断进程 (control+c)
    SIGTERM 终止进程 软件终止信号
    SIGKILL 终止进程 杀死进程
    SIGALRM 闹钟信号

    import os    
    import signal    
    from time import sleep    
         
    def onsignal_term(a,b):    
        print '收到SIGTERM信号'    
         
    #这里是绑定信号处理函数,将SIGTERM绑定在函数onsignal_term上面    
    signal.signal(signal.SIGTERM,onsignal_term)    
         
    def onsignal_usr1(a,b):    
        print '收到SIGUSR1信号'    
    #这里是绑定信号处理函数,将SIGUSR1绑定在函数onsignal_term上面    
    signal.signal(signal.SIGUSR1,onsignal_usr1)    
         
    while 1:    
        print '我的进程id是',os.getpid()    
        sleep(10)
    

    在另一个终端中执行

    import os    
    import signal    
         
    #发送信号,16175是前面那个绑定信号处理函数的pid,需要自行修改    
    os.kill(16175,signal.SIGTERM)    
    #发送信号,16175是前面那个绑定信号处理函数的pid,需要自行修改    
    os.kill(16175,signal.SIGUSR1)
    

    上述示意了简单的信号功能,可以用来自定义捕捉信号并执行相应的函数。使用信号需要特别注意的地方:如果一个进程收到一个SIGUSR1信号,然后执行信号绑定函数,第二个SIGUSR2信号又来了,第一个信号没有被处理完毕的话,第二个信号就会丢弃。所以,尽量不要在多线程中使用信号。

    argparse模块

    命令行解析工具,new in 2.7

    import argparse
    
    parser = argparse.ArgumentParser(description='Process some integers.')
    parser.add_argument('integers', metavar='N', type=int, nargs='+',
                        help='an integer for the accumulator')
    parser.add_argument('--sum', dest='accumulate', action='store_const',
                        const=sum, default=max,
                        help='sum the integers (default: find the max)')
    
    args = parser.parse_args()
    print args.accumulate(args.integers)
    

    首先,创建一个 ArgumentParser
    对象,
    parser = argparse.ArgumentParser(description='Process some integers.')
    然后添加参数,实现参数添加是通过调用函数add_argument,该函数告诉ArgumentParser对象将新添加的参数保存为attributes,最后parse_args()会返回该对象且该对象新增了对应添加的属性。

    主要method分析

    ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
    • name or flags - Either a name or a list of option strings, e.g. foo
      or -f, --foo
    • action - The basic type of action to be taken when this argument is encountered at the command line.
    • nargs - The number of command-line arguments that should be consumed.
    • const - A constant value required by some action and nargs selections.
    • default - The value produced if the argument is absent from the command line.
    • type - The type to which the command-line argument should be converted.
      choices - A container of the allowable values for the argument.
    • required - Whether or not the command-line option may be omitted (optionals only).
    • help - A brief description of what the argument does.
    • metavar - A name for the argument in usage messages.
    • dest - The name of the attribute to be added to the object returned by parse_args()
      .

    详细用法查阅

    nargs

    parser.add_argument('--foo', nargs=2),最为简单用法,表示--foo 需要两个参数,如果传递的参数不对,则会引发error

    parser.add_argument('--foo', nargs='?', const='c', default='d'),从命令行传递至多一个参数,如果没有则用default指定的。‘?’相对更灵活,当命令行没有参数输入时程序不会出错,使用默认的。而上面指定个数后输入参数不对程序直接挂掉。

    nargs='*' 不直接指定个数,传入的参数表示成一个列表。

    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('--foo', nargs='*')
    >>> parser.add_argument('--bar', nargs='*')
    >>> parser.add_argument('baz', nargs='*')
    >>> parser.parse_args('a b --foo x y --bar 1 2'.split())
    Namespace(bar=['1', '2'], baz=['a', 'b'], foo=['x', 'y'])
    

    nargs = '+' 很像'*',但最低要求一个参数,当没有参数时报错。而‘’可以没有参数。
    总结来说nargs常规用法就是硬性指定参数个数,‘?’,‘
    ’,‘+’则提供了更多的灵活性,也是程序中较常用的。

    action

    大部分action完成的动作就是为对象添加该属性,默认的动作是‘store’,parser.parse_args()返回的对象具有新增属性,如

    args = parser.parse_args()
    print args.accumulate(args.integers)
    

    'store_const' --This stores the value specified by the const keyword argument.
    'store_true' and 'store_false' 类似于上面的’store_const‘,只是存储的是True和False。
    'append',允许多次指定一个参数,返回该参数列表集合。

    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('--foo', action='append')
    >>> parser.parse_args('--foo 1 --foo 2'.split())
    Namespace(foo=['1', '2'])
    
    type

    ‘type’,默认是以字符串读入命令行的参数,但实际中还会要有int 、float等类型的数据输入,这时可以通过type指定。

    >>> import argparse
    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('foo', type=int)
    _StoreAction(option_strings=[], dest='foo', nargs=None, const=None, default=None, type=<type 'int'>, choices=None, help=None, metavar=None)
    >>> parser.add_argument('bar', type=file)
    _StoreAction(option_strings=[], dest='bar', nargs=None, const=None, default=None, type=<type 'file'>, choices=None, help=None, metavar=None)
    >>> ar = parser.parse_args('2 myfile.txt'.split())
    >>> ar.foo
    2
    >>> ar.bar
    <open file 'myfile.txt', mode 'r' at 0x7f352c089300>
    >>> ar.bar.read()  //返回的是字符串
    'myfile.txt\n'
    

    上述程序中有以下两点注意:一是parser.parse_args()函数返回的对象具有了新增加的属性,二是可以进行整个文件的输入操作,只要通过获取属性的方法就能获取到文件句柄,十分方便。

    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('bar', type=argparse.FileType('w'))
    >>> parser.parse_args(['out.txt'])
    Namespace(bar=<open file 'out.txt', mode 'w' at 0x...>)
    

    通过 type=argparse.FileType('w') 完成文件打开方式,默认的type=file为只读模式。
    type还能是函数名,实现传递函数的功能。

    choices
    >>> parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
    >>> parser.parse_args(['rock'])
    Namespace(move='rock')
    

    给参数限定了范围,如上述中参数 move只能是三者之一。

    required
    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('--foo', required=True)
    >>> parser.parse_args(['--foo', 'BAR'])
    Namespace(foo='BAR')
    >>> parser.parse_args([])
    usage: argparse.py [-h] [--foo FOO]
    argparse.py: error: option --foo is required
    

    表示必选参数。

    help

    指定 -h时显示帮助信息,以下两种方式可以显示帮助信息
    parser.print_help()
    parser.parse_args(['-h'])

    metavar
    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('--foo', metavar='YYY')
    >>> parser.add_argument('bar', metavar='XXX')
    >>> parser.parse_args('X --foo Y'.split())
    Namespace(bar='X', foo='Y')
    >>> parser.print_help()
    usage:  [-h] [--foo YYY] XXX
    
    positional arguments:
     XXX
    
    optional arguments:
     -h, --help  show this help message and exit
     --foo YYY
    
    dest

    该参数决定了parser.parse_args()函数返回后增加的属性名,若不指定则一般用第一个参数指定。

    struct 模块

    参考学习来源
    实现python值和结构体之间的转换,当网络中传递字符串时很方便,但结构体由于包含int、char等不同类型,需要一种机制将其打包成二进制流的字符串。
    struct模块中最主要的三个函数式pack()、unpack()、calcsize()。
    pack(fmt, v1, v2, ...) ------ 根据所给的fmt描述的格式将值v1,v2,...转换为一个字符串。
    unpack(fmt, bytes) ------ 根据所给的fmt描述的格式将bytes反向解析出来,返回一个元组。
    calcsize(fmt) ------ 根据所给的fmt描述的格式返回该结构的大小。

     struct header
    
          {
    
              unsigned short  usType;
    
              char[4]               acTag;
    
              unsigned int      uiVersion;
    
              unsigned int      uiLength;
    
          };
    
     # 在C语言对将该结构体封装到一块缓存中是很简单的,可以使用memcpy()实现。在Python中,使用struct就需要这样:
    
    str = struct.pack('B4sII', 0x04, 'aaaa', 0x01, 0x0e)
    
     # 'B4sII'  ------   有一个unsigned short、char[4], 2个unsigned int。其中s之前的数字说明了字符串的大小 。
    type, tag, version, length = struct.unpack('B4sll', str)
    

    常用的格式有以下:


    image.png

    除了上述直接调用struct模块中的函数,struct封装了一个类,class struct.Struct(format),该类中同样封装了pack,unpack等method,但是封装格式在struct.Struct(format)中format参数指定,不用再method中指定。该类的效率更高因为只需要编译一次。

    Creating a Struct object once and calling its methods is more efficient than calling the struct
    functions with the same format since the format string only needs to be compiled once.

    import struct
    import binascii
    values = (1, 'abc', 2.7)
    s = struct.Struct('I3sf')
    packed_data = s.pack(*values)
    unpacked_data = s.unpack(packed_data)
    print 'Original values:', values
    print 'Format string :', s.format
    print 'Uses :', s.size, 'bytes'
    print 'Packed Value :', binascii.hexlify(packed_data)
    print 'Unpacked Type :', type(unpacked_data), ' Value:', unpacked_data
    

    输出:
    Original values: (1, 'abc', 2.7)
    Format string : I3sf
    Uses : 12 bytes
    Packed Value : 0100000061626300cdcc2c40
    Unpacked Type : <type 'tuple'> Value: (1, 'abc', 2.700000047683716)

    扩展:计算机存储格式

    打包的后的字节顺序默认上是由操作系统的决定的,当然struct模块也提供了自定义字节顺序的功能,可以指定大端存储、小端存储等特定的字节顺序,对于底层通信的字节顺序是十分重要的,不同的字节顺序和存储方式也会导致字节大小的不同。在format字符串前面加上特定的符号即可以表示不同的字节顺序存储方式,例如采用小端存储 s = struct.Struct(‘<I3sf’)就可以了。

    image.png

    若没有指定,默认为@,即本地默认字节序,不同系统不一样,可以通过sys.byteorder查看。网络字节序是大端格式,用!以防自己记不起来大端还是小端。

    fcntl 模块

    根据文件描述符对文件或IO进行控制,
    fcntl.flock(f,fcntl.LOCK_EX)
    这样就对文件test加锁了,如果有其他进程对test文件加锁,则不能成功,会被阻塞,但不会退出程序。
    解锁:
    fcntl.flock(f,fcntl.LOCK_UN)

    flock() : flock(f, operation)
    operation : 包括:
    fcntl.LOCK_UN 解锁
    fcntl.LOCK_EX 排他锁
    fcntl.LOCK_SH 共享锁
    fcntl.LOCK_NB 非阻塞锁
    LOCK_SH 共享锁:所有进程没有写访问权限,即使是加锁进程也没有。所有进程有读访问权限。
    LOCK_EX 排他锁:除加锁进程外其他进程没有对已加锁文件读写访问权限。
    LOCK_NB 非阻塞锁:
    如果指定此参数,函数不能获得文件锁就立即返回,否则,函数会等待获得文件锁。LOCK_NB可以同LOCK_SH或LOCK_NB进行按位或(|)运算操作。 fcnt.flock(f,fcntl.LOCK_EX|fcntl.LOCK_NB)

    相关文章

      网友评论

          本文标题:wifijammer源码分析二

          本文链接:https://www.haomeiwen.com/subject/bxxltxtx.html