zoukankan      html  css  js  c++  java
  • 读书笔记_python网络编程3_(2)

    2.UDP

    2.0.数据包表示较短的信息,大小通常不会超过几千字节,在浏览器与服务器进行会话/电子邮件客户端与ISP的邮件服务器进行会话时,这些独立而小型的数据包是如何组成会话的呢?

    2.0.1.IP协议只负责尝试将每个数据包传输至正确的机器,如果两个独立的应用程序要维护一个会话的话,还需要两个额外的特性。这两个特性是由IP层以上的协议来提供的。

    2.0.1.1. 多路复用(multiplexing):为两台主机之间传送的大量数据包打上标签,就可以将表示网页的数据包和用于电子邮件的数据包分开来,这两种数据包也可以与其他网络会话使用的数据包分割开。

    2.0.1.2. 可靠传输(reliable transport):

    1)两台主机间独立传输的数据包流发生的任何错误,都需要进行修复;
    2)丢失的数据包也需要进行重传,知道成功发送至目的地址
    3)如果数据包到达时顺序错乱,则要将这些数据包重组会正确的顺序
    4)丢弃重复的数据包,保证数据流中的信息没有荣誉

    2.0.2.使用IP层的两个主要协议UPD和TCP:

    2.0.2.1. 用户数据报协议(UDP),只解决多路复用的问题,UDP协议提供了一个端口号,用于对目标为同一机器上,不同服务的多个数据包进行适当的多路分解。虽然支持了多路复用和分解,但是使用UDP协议的网络程序仍然需要自己处理丢包、重包和包的乱序问题。

    2.0.2.2. 传输控制协议(TCP), TCP解决了多路复用+可靠传输,同样使用端口号来多路复用和分解。此外,还保证了数据流的顺序及可靠传输。尽管连续的数据流在传输时被分为多个数据包,在接收端再进行重组,但是这些细节都对应用层隐藏了。

    2.0.3.有一些专用应用程序再IP网路进行会话时,两者都不选,而是创建一个全新的基于IP的协议来支持新的会话方式。这种应用较少,如在局域网内所有主机间共享的多媒体应用,少见且不太可能使用Py编写进行底层操作的程序。

    2.0.4.不太可能在自己的任何一个应用程序中使用UDP,如果认为UDP使用某个应用,不妨了解一下消息队列。对UDP的介绍可以了解原始数据包的多路复用。

    2.1.端口号

    计算机网络和电磁信号理论中,对共享同一通信信道的多个信号,进行区分是个常见的问题。
    

    2.1.2.多路复用就是允许多个会话共享同一介质的一种解决方案,UDP方案为每个UDP数据包分配了一对无符号16位端口号(port number),范围从0到65536。

    2.1.3.源端口(source port)标识了源机器上发送数据包的特定进程或程序,而目标端口(destination port)标识了目标IP地址上,进行该会话的特定应用程序。

    IP网络层上,唯一可见的就是向特定主机传输的数据包。
    Source IP --> Destination IP
    进行通信的两台机器上的网络栈,需要支持多个程序互不影响地同时进行交互,故同时使用IP和端口号来标识源机器与目标机器。
    Source (IP : port num) --> Destination (IP :port num)
    对于同一特定会话中发送的数据包,4个值完全相同,响应数据包则把源IP地址与目标IP地址调换,把源端口与目标端口号调换。
    

    2.1.4.示例:

    1. 在IP为192.168.1.9机器上架设一台DNS服务器,向操作系统请求使用53端口的权限(标准DNS端口),用来接收发送至UDP端口的数据包。
    2. IP为192.168.1.30的客户机想查询服务器。客户机在内存中构造该查询,请求操作系统将查询封装为UDP数据包并发送,到达目标地址时,目的地址的程序,识别发送该查询的客户端并返回响应,而客户端没有声明请求端口号,则随机分配44137号端口
      则数据包通过Source(192.168.1.30:44137) -> Destination (192.168.1.9:53)
      发送至目标地址的53号端口。
      DNS服务器构造完成了相应信息,请求操作系统发送UDP数据包作为响应。该数据包中的源目标IP和端口与请求数据包正好想法,故,服务器的响应就能直接发回给请求方了。
      Source(192.168.1.9:53) -> Destination (192.168.1.30:44137)
      UDP机制相当简单,仅使用IP地址和端口号进行识别,将数据包发送至目标地址。
      3)客户端如何获悉需要连接的端口号?

    2.1.4.1. 惯例:互联网号码分配机构(IANA)为许多专用服务分配了官方知名端口

    2.1.4.2. 自动配置: 通常计算机连接网络时,会使用DHCP协议来获取一些重要的服务的IP地址,如DNS,应用程序通过将这些IP与知名端口结合,便可访问这些基础服务。

    2.1.4.3. 手动配置:管理员用户必须手动配置IP地址或相应的服务域名,每当浏览器键入服务器名时,都发生了手动配置的对应操作。

    2.1.5.IANA将端口号分为了三大类,对UDP及TCP端口号均使用。

    2.1.5.1. 知名端口号(0~1023)分配给最重要/常用的服务。类Unix上,普通用户

    无法监听这些端口,可以防止将一些运行程序伪装成重要的系统服务,可以在主机提供公司分发Linux命令行账户时,排上用处。

    2.1.5.2. 注册端口(1024~49151)在操作系统层无特别之处,如可以编写程序占用5432端口,并伪装为一个PostgreSQL数据库服务,IANA可以为一些专用服务注册这些端口,故建议只在使用其指定服务时才使用这些端口。

    2.1.5.3. 其余端口(49152~65535)随意使用。当客户端无需为其提供的服务指定特定端口号时,OS便会从这些端口号组成的端口池中随机选取端口号用于该服务。

    2.1.6.当编写程序从命令行/配置文件等用户输入接收端口号时,如果希望程序对用户友好,除了接收数字表示的端口号外,还应接收可读性较高的知名端口名。可使用Py标准库socket模块的getservbyname()函数获取,可以使用如下方法查询域名服务的端口号:

    import socket
    socket.getservbyname('domain')
    O]53
    

    同样可以使用socket模块提供的更复杂的getaddrinfo()函数来解析端口名。

    2.1.7. Linux和Mac上,知名服务器与其对应的端口号通常保存在/etc/services文件中,前几页仍然是老版本的协议,其中包含的预留端口号已经不再使用,IANA在http://www.iana.org/assignments/port-numbers维护了最新在线版本

    2.2.套接字

    在决定如何设计网络编程API时,Py没有重复造轮子。在底层,标准库对兼容POSIX系统的,网络操作的底层系统进行了封装,并为所有普通原始调用,提供基于对象的接口。封装后的Py函数名与原始系统调用名相同。故可以使用熟知的方法来调用传统系统,在需要时进行底层操作系统调用,无需使用特定语言API,提供和C相同的系统调用集合。
    无论是Win还是POSIX(Linux/Mac OS),网络操作背后的系统调用都是围绕着套接字(socket)来进行的。套接字是一个通信断点,OS使用整数来标识套接字,而Py使用socket.socket表示。该对象内部维护了OS标识套接字的整数(可调用fileno()方法查看)。当调用socket.socket对象的方法,请求使用该套接字的系统调用时,该对象都会自动使用,内部维护的套接字整数标识符。
    

    2.2.0.POSIX中,表示套接字的fileno()整数也是一个文件描述符(file descriptor),可以从表示所有打开文件的整数池中获取。在POSIX中使用这一整数做一些非网络调用,如使用文件描述符进行os.read()和os.write()的文件操作,该文件操作符实际上表示的就是套接字。

    2.2.0.1. 实际使用中的套接字是什么样的?

    # 2-1 /chapter02/udp_local.py
    import argparse, socket
    from datetime import datetime
    
    MAX_BYTES = 65535   # 64KB,3w2k个汉字
    
    def server(port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind(('127.0.0.1', port))
        print('Listening at {}'.format(sock.getsockname()))
        while True:
            data, address = sock.recvfrom(MAX_BYTES)
            text = data.decode('ascii')
            print('The client at {} says {!r}'.format(address, text))
            data = text.encode('ascii')
            sock.sendto(data, address)
    
    def client(port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        text = 'The time is {}'.format(datetime.now())
        data = text.encode('ascii')
        sock.sendto(data, ('127.0.0.1', port))
        print('The OS assigned me the address {}'.format(sock.getsockname()))
        data, address = sock.recvfrom(MAX_BYTES) # Danger!
        text = data.decode('ascii')
        print('The server {} replied {!r}'.format(address, text))
    
    if __name__ == '__main__':
        choices = {'client': client, 'server': server}
        parser = argparse.ArgumentParser(description='Send and receive UDP locally')
        parser.add_argument('role', choices=choices, help='which role to play')
        parser.add_argument('-p', metavar='PORT', type=int, default=1060, help='UDP port (default 1060)')
        args = parser.parse_args()
        funciont = choices[args.role]
        funciont(args.p)
    

    2.2.0.2. 服务器使用socket()调用创建了一个空套接字,标记了所属特定类别:协议族AF_INET及数据报类型SOCK_DGRAM(表示在IP网络上使用UDP,表示应用层数据块传输的术语)

    2.2.0.3. 服务器使用bind()命令请求绑定一个UDP网络地址,该网络地址有Py元组构成,包含IP和端口号,如果该端口号被占用,则抛出异常。

    2.2.0.4.可以使用socket对象的getsockname()来查询套接字目前绑定到的IP及端口的元组

    2.2.0.5. 套接字成功绑定,服务器就准备好接受请求,会进入循环,不断运行recvfrom(),可接收最长65535字节信息,服务器将接收每个数据报的完整内容,没有收到客户端发送的请求信息前,recvfrom()永远保持等待。

    2.2.0.6. 接收到一个数据报,recvfrom()会返回 1)发送该数据报的客户端地址;2)以字节表示的数据报内容;将字节转换为字符串,在控制台输出,再向客户端发挥一个响应数据报。

    2.2.0.7. 服务器仍然在运行,打开系统的另一个命令行窗口,连续运行两次客户端程序:

    (venv) >python udp_local.py client
    O]
    The OS assigned me the address ('0.0.0.0', 63042)
    The server ('127.0.0.1', 1060) replied 'The time is 2019-11-27 20:33:33.395549'
    
    (venv) >python udp_local.py client
    O]
    The OS assigned me the address ('0.0.0.0', 63053)
    The server ('127.0.0.1', 1060) replied 'The time is 2019-11-27 20:33:43.011358'
    

    2.2.0.8. 客户端的sendto()调用提供了两个信息:1)要发送的信息 2)目标地址。这是向服务器发送数据包所必需的唯一调用。操作系统对客户端的IP地址和端口号进行了自动分配。通过getsockname()调用查看IP和port。

    2.2.1. 混杂客户端与垃圾回复:不考虑地址是否正确,接收并处理所有收到的数据包的网络监听客户端。

    尽管recvfrom()返回了传入的数据包地址,但是代码没有检查该数据报的源地址,即没有验证该数据报是否确实是服务器发回的响应。
    把服务器的响应延迟一段时间,Win需要再服务器接受请求和发送响应两步间添加一个time.sleep();Linux和Mac,键入Ctrl+Z就可以暂停服务器
    
    $ python3 udp_local.py server
    O]
    Listening at ('127.0.0.1', 1060)
    ^Z
    [1]+  Stopped                 python3 udp_local.py server
    

    运行客户端,客户端会发送数据报,过期等待,直到收到服务器的响应

    $ python3 udp_local.py client
    O]
    The OS assigned me the address ('0.0.0.0', 47935)
    
    攻击者伪造一个服务器的响应,在服务器返回真实响应前,先发送伪造的数据报。客户端会接收任何数据报,没有对响应做任何检查,则客户端认为伪造的响应来自服务器。Py中启动快速会话来发送一个伪造的数据包
    
    >>> import socket
    >>> sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    >>> sock.sendto('FAKE'.encode('ascii'), ('127.0.0.1', 47935))
    O]
    4
    
    客户端会结束等待,将该第三方响应看做是服务器的响应。
    
    The server ('127.0.0.1', 55437) replied 'FAKE'
    
    键入fg将暂停的服务器恢复运行;
    

    2.2.1.1. 客户端面对任何可以向其发送UDP数据包的终端都是脆弱的,与中间人攻击不同,中间人攻击是取得了网络控制权后,从非法地址发送伪造数据包,这种情况下的通信只能通过加密来保护。

    2.2.1.2. 进行网络监控时,需要监控到达某一接口的所有数据包。

    2.2.1.3. 使用编写良好的优秀加密方法,可以保证程序与正确的服务器进行通信。无法做到时,可以使用两个快速解决方法:

    1. 设计/使用在请求中包含唯一标识符/请求ID的协议,响应中重复特定请求的唯一标识符/请求ID,只要ID的取值范围足够大,就无法在短时间内简单地把所有可能正确的ID数据包都发一遍,故,攻击者虽然知道信息,但还需构造正确的ID
    2. 检查响应数据包的地址与请求数据包的地址是否相同,也可用connect()来阻止其他地址想客户端发送数据包。

    2.2.2.不可靠性、退避、阻塞和超时

    如果数据包丢失,代码会变得复杂,如下,该程序中的服务器未始终响应客户端的请求,而是随机选择,只对收到的一半客户端请求作出相应。
    
    # 2-2 chapter02/udp_remote.py
    import argparse, random, socket, sys
    
    MAX_BYTES = 65535
    
    def server(interface, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((interface, port))
        print('Listening at', sock.getsockname())
        while True:
            data, address = sock.recvfrom(MAX_BYTES)
            if random.random() < 0.5:
                print('Pretending to drop packet from {}'.format(address))
                continue
            text = data.decode('ascii')
            print('The client at {} says {!r}'.format(address, text))
            message = 'Your data was {} bytes long'.format(len(data))
            sock.sendto(message.encode('ascii'), address)
    
    def client(hostname, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        hostname = sys.argv[2]
        sock.connect((hostname, port))
        print('Client socket name is {}'.format(sock.getsockname()))
        delay = 0.1 # seconds
        text = 'This is another message'
        data = text.encode('ascii')
        while True:
            sock.send(data)
            print('Waiting up to {} seconds for a reply'.format(delay))
            sock.settimeout(delay)
            try:
                data = sock.recv(MAX_BYTES)
            except socket.timeout:
                delay *= 2 # wait even longer for the next request
                if delay > 2.0:
                    raise RuntimeError('I think the server is down')
            else:
                break # we are done, and can stop looping 
        print('The server says {!r}'.format(data.decode('ascii')))
        
    if __name__ == '__main__':
        choices = {'client':client, 'server': server}
        parser = argparse.ArgumentParser(description='Send and receive UDP, pretending packets are often dropped')
        parser.add_argument('role', choices=choices, help='which role to take')
        parser.add_argument('host', help='interface the server listens at ;host the client sends to')
        parser.add_argument('-p', metavar='PORT', type=int, default=1060, help='UDP port (default 1060')    
        args = parser.parse_args()
        function = choices[args.role]
        function(args.host, args.p)
    

    2.2.2.1. 可以把服务器的IP指定为一个空字符串,能使得服务器更为通用。空字符串表示"任何本地接口"

    $ python3 udp_remote.py server ""
    O]
    Listening at ('0.0.0.0', 1060)
    
    服务器每次收到请求,使用random()来决定是否对该请求作出应答,无需不断运行客户端来等待真正丢包现象的发生,无论服务器是否响应,都会输出一条信息,就能跟踪服务器的运行状态了。
    

    2.2.2.2. UDP不不可靠性意味着客户端必须在一个循环内发送请求。客户端可以选择永远等待某个请求的响应,也可重新发送另一个请求,客户端无法区分

    1)响应时间较长,但即将传回客户端
    2)响应或请求在传输中丢失了,无法到达客户端
    3)服务器宕机,无法响应
    故,UDP客户端必须选择一个等待时间。一旦超过,就重发,因此,客户端不会在调用recv()后永久暂停,而是调用套接字的settimeout()方法,告诉OS,客户端进行一个套接字操作的最长等待时间是delay秒,一旦超过,抛出socket.timeout异常,recv()中断

    2.2.2.3. 一个调用会等待网络操作完成,则该调用阻塞(block)了调用方,描述像recv()这样的调用。只要没收到新数据,cli就一直等待下去。

    2.2.2.4. 丢包的主要原因是网络拥塞,最不想做的事就是不断重发一个可能会丢失的数据包,可使用指数退避(exponential backoff),尝试重发数据包的频率会越来越低,拥塞的网络有可能在丢弃一些请求和响应数据包后恢复正常。最基本的方法是:在每次无法收到响应时,将等待时间间隔翻倍。

    2.2.2.5. 如果编写一个长连接的UDP客户端,可以记录最近几次请求完成花费的时间,就可以给服务器足够的时间做出响应,然后再重发。

    $ python3 udp_remote.py client hostname
    Client socket name is ('127.0.0.1', 55861)
    Waiting up to 0.1 seconds for a reply
    Waiting up to 0.2 seconds for a reply
    The server says 'Your data was 23 bytes long'
    $ python3 udp_remote.py client hostname
    Client socket name is ('127.0.0.0', 59263)
    Waiting up to 0.1 seconds for a reply
    The server says 'Your data was 23 bytes long'
    
    所有的丢包都是服务器模拟的,如果服务器宕机,无法通过UDP来判断,故,客户端的最佳时间就是在进行足够多的尝试后放弃,结束服务器进程,然后重新运行客户端
    

    2.2.2.6. 需要不断重发数据包的守护程序,不能严格遵循指数退避的策略,否则延迟会快速增加到一个较大的数字,可以选择最大延时参数,如5分钟

    2.2.3. 连接UDP套接字

    2.2.3.1.

    1. 显示的bind()调用:发生在serv,指定serv要使用的IP和port
    2. 隐式绑定:发生在cli,第一次尝试使用一个socket时,OS会随机分配临时port

    2.2.3.2. 套接字操作connect(),如果使用sendto(),那么每次向Serv发送信息时,都必须显示给出服务器的IP和port,如果使用connect,则OS事先就已经知道数据包要发送到的远程地址,可以简单地把要发送的数据作为参数传入send()调用,无需重复给出Serv的IP;此外,还解决了客户端的混杂性问题,只要OS发现传入数据包的返回地址与已连接的地址不同,就会将该数据包丢弃。

    2.2.3.3. 两种方法编写对响应数据包返回地址进行仔细检查的UDP客户端:

    1)使用sendto()指定每个数据包的目标IP,使用recvfrom()接收响应,并检查响应数据包的返回IP,看是否曾经向该地址发送过请求。
    2)创建socket后,用connect()与其目标IP链接,使用send()和recv()进行通信。OS会将不需要的数据包过滤,只支持同时与一台服务器交互的情况,因为同一socket上重复运行connect()不会增加目标IP。

    2.2.3.4. 使用connect()连接一个UDP套接字后,可使用socket的getpeername()得到所连接的地址。如果在尚未连接的socket上调用,会抛出socket.error

    2.2.3.5.

    1. connect()连接UDP的socket,只是简单地将连接的IP写入OS的内存,供之后调用send()和recv()使用
    2. 使用connect()/通过返回地址手动过滤不需要的数据包,并不能确保安全,攻击者很容易伪造出拥有Serv返回IP的数据包,这种使用另一台PC的返回IP来发送数据包的行为叫做电子欺骗(spoofing),协议设计者在设计安全协议时需要考虑的首要问题

    2.2.4. 请求ID

    如果自己设计一套UDP请求和响应机制,需要给每个请求加上一个序列号,保证接收的响应包含相同的序列号。Serv端,只需把请求的ID复制到相应的响应中。
    
    1. 使用指数退避的Cli会重复发送请求,请求ID可以将响应与重复发送的请求正确对应起来;还能解决比较罕见的重复问题:网络结构的冗余,偶尔也会在Serv和Cli间产生同一数据包的两个脚本,造成的重复问题也可以由请求ID解决。
    2. 一个范围合适且使用random生成的0~N的请求ID可以使Cli接收伪造响应的可能性大大降低。单仍然不是真正的安全,只有在攻击者无法获取网络通信信息,只能进行最简单的电子欺骗攻击时,才能起到保护作用,真正的安全意味着,攻击者可以获取通信数据,并插入任何信息,我们的客户端仍然能受到保护。

    2.3. 绑定接口

    1. Serv选择'127.0.0.1'表示只接收来自本机上其他运行程序的数据包
    2. 使用空字符串''作为通配符,接收通过该服务器的任何网络接口收到的数据包
    3. 提供该Serv的某个外部IP接口的IP,服务器只会监听传输至该IP的数据包
    $ python udp_remote.py server 192.168.5.130
    Listening at ('192.168.5.130', 1060)
    

    使用另一台机器仍然可以连接到这一IP

    $ python udp_remote.py client hostname
    Client socket name is ('192.168.5.10', 35084)
    Waiting up to 0.1 seconds for a reply
    The server says 'Your data was 23 bytes'
    

    同一台机器上运行Cli脚本,并通过自环接口来连接Serv的话,Serv永远不会做出响应

    $python udp_remote.py client 127.0.0.1
    Client socket name is ('127.0.0.1', 60251)
    Waiting up to 0.1 seconds for a reply
    Traceback (most recent call last):
        ...
    socket.error: [Errno 111] Connection refused
    
    实际上Serv还是会发送数据包,OS会检测是否有打开的port没有像网络发送数据包,如果有请求发送至该port,那么OS会马上响应,通知Cli该连接是不可用的。只有使用自环接口时,UDP才能够返回"拒绝连接"的响应。真实网络上通信,Cli发送的数据包无法判断Serv是否有接受接受该请求的port。
    

    使用同一机器再次运行客户端,使用外部IP连接Serv

    $ python udp_remote.py client 192.168.5.130
    Client socket name is ('192.168.5.130', 34919)
    Waiting up to 0.1 seconds for a reply
    The server says 'Your data was 23 bytes'
    
    本地的运行程序可以使用任何本机的IP向Serv发送请求,即使使用该IP地址与本机上的其他服务通信。
    

    2.3.1. 绑定IP接口会限制与Serv进行通信的外部主机,只要本机上的其他Cli知道IP,则与Serv的本地通信不会限制。

    不能通过自环接口/通配符IP(原因是通配符包含了127.0.0.1)来运行第二个Serv,可以使用第一个Serv没有监听的外部IP接口,如果机器有多个远程接口,可以启动更多Serv,每个Serv监听一个远程接口。

    $python udp_remote.py server 127.0.0.1
    $python udp_remote.py server 127.0.0.1
    Traceback (most recent call last):
        ...
    OSError: [Errno 98] Address alredy in use
    $python udp_remote.py server
    Traceback (most recent call last):
        ...
    OSError: [Errno 98] Address alredy in use
    $python udp_remote.py server 192.168.5.130
    Listening at ('192.168.5.130', 1060)
    

    使用UDP的Cli发送数据包,每个请求只会被一个Serv接收,该Serv的IP就是对应UDP请求数据包的特定目标IP。

    2.3.2. IP网络栈不会把UDP端口看做是可以连接/正在使用的单独实体,而关注的是UDP"socket名(IP+Port)",必须保证socket名不会与正在监听的Serv产生冲突。

    2.4. UDP分组

    较大的数据包在传输过程中更易发生丢包现象,因为只要分隔出的任一小数据包没有传至目标IP,便无法重组出原始的大数据包,正在监听的OS就无法正确接收了。
    

    2.4.1. 对大型UDP数据包进行分组,使之与传输线路兼容过程对应用程序不可见,以下3种情况,可能会有关联

    2.4.1.1. 会对协议作出限制。如,只允许传输小数据包以减少重传情况,或限制远程IP栈用于,重组UDP数据包+传回应用程序的时间。

    2.4.1.2. 运行防火墙,本地主机可以自动检测出于远程主机间的MTU(最大传输单元/数据包容量),若防火墙错误阻止了ICMP数据包,较大的UDP数据包会被遗忘。

    2.4.1.3. 通过OS关闭分组功能,这样UDP数据包过大,就会受到一条错误信息。

    # 2-3 chapter02/big_sender.py
    import IN, argparse, socket
    
    if not hasattr(IN, 'IP_MTU'):
        raise RuntimeError('cannot perform MTU discovery on this combination of operating system and Python distribution')
    
    def send_big_datagram(host, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.IPPROTO_IP, IN.IP_MTU_DISCOVER, IN.IP_PMTUDISC_DO)
        sock.connect((host, port))
        try:
            sock.send(b'#' * 65000)
        except socket.error:
            print('Alas, the datagram did not make it')
            max_mtu = sock.getsockopt(socket.IPPROTO_IP, IN.IP_MTU)
            print('Actual MTU: {}'.format(max_mtu))
        else:
            print('The big datagram was send!')
        
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(description='Send UDP packet to get MTU')
        parser.add_argument('host', help='the host to which to target ther patcket')
        parser.add_argument('-p', metavar = 'PORT', type=int, default=1060, help='UDP port (default 1060')
        args = parser.parse_args()
        send_big_datagram(args.host, args.p)
    

    运行该程序,只允许发送不草果1500B的物理数据包

    $ python big_sender.py hostname
    Alas, the datagram did not make it
    Actual MTU:1500
    

    2.5. 套接字选项

    可以通过getsockopt()和setsockopt()获取并设置套接字选项。
    

    设置套接字选项,设置套接字选项的调用比获取套接字选项的调用多一个参数

    1. 给出所属选项组的名称
    2. 给出要设置的选项名
    value = s.getsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, vale)
    

    2.5.1. SO_BROADCAST: 允许发送并接受UDP广播数据包

    2.5.2. SO_DONTROUTE: 只向本级直接连接的子网内的主机发送数据包。

    2.5.3. SO_TYPE: 该选项传给getsockopt(),会返回套接字类型,可以从中得知使用的是用于UDP的SOCK_DGRAM类型还是支持TCP语义的SOCK_STREAM类型

    2.6. 广播

    通过广播,可以将数据报的目标地址设置为本机连接的整个子网,然后使用物理网卡将数据报广播,这样就无需再复制该数据包并单独将其发送给所有连接至该子网的主机了。
    

    由于出现了更为

  • 相关阅读:
    利用索引提高SQL Server数据处理的效率
    拆掉思维里的墙摘抄
    QR码和PDF417比较
    保存一个记录到数据库又马上返回ID号
    C#获取当前路径的7种方法
    汉诺塔算法不错,收藏了!
    如何确定Z检验的值(查正态分布表时要注意中间的数字都是面积,最左边一列和最上面一行都是Z值)
    opencv的安装
    SQL事务
    重大财务决策前的思考
  • 原文地址:https://www.cnblogs.com/wangxue533/p/11939457.html
Copyright © 2011-2022 走看看