代码之家  ›  专栏  ›  技术社区  ›  anoneemus

Python套接字窃取彼此的数据包

  •  4
  • anoneemus  · 技术社区  · 7 年前

    我正试图编写一个程序来测试不同大小数据包的并行数据传输速度。不过,我注意到一些奇怪的事情,根据我的程序,数据包的大小似乎对传输时间没有影响,而Unix ping 二进制文件会在我使用的一些数据包大小上超时。我发送了4个包含字符串“testquest”的数据包,其中一个只有2000字节,设置为0。然而,当我打印结果时 全部的 包含“testquest”(远小于2000字节)。我唯一能得出的结论是,这些套接字都以某种方式接收相同的数据包,这可以解释为什么它们都具有相同的rtt。

    我制作这个MCVE是为了说明这个问题(您可以忽略“checksum”函数,它是为了完整性而包含的,但根据经验我知道它是有效的):

    #!/usr/bin/env python3
    import socket
    import struct
    import time
    from multiprocessing.pool import ThreadPool as Pool
    from sys import argv, byteorder
    
    def calculate_checksum(pkt):
        """
        Implementation of the "Internet Checksum" specified in RFC 1071 (https://tools.ieft.org/html/rfc1071)
        Ideally this would act on the string as a series of 16-bit ints (host
        packed), but this works.
        Network data is big-endian, hosts are typically little-endian,
        which makes this much more tedious than it needs to be.
        """
    
        countTo = len(pkt) // 2 * 2
        total, count = 0, 0
    
        # Handle bytes in pairs (decoding as short ints)
        loByte, hiByte = 0, 0
        while count < countTo:
            if (byteorder == "little"):
                loByte = pkt[count]
                hiByte = pkt[count + 1]
            else:
                loByte = pkt[count + 1]
                hiByte = pkt[count]
            total += hiByte * 256 + loByte
            count += 2
    
        # Handle last byte if applicable (odd-number of bytes)
        # Endianness should be irrelevant in this case
        if countTo < len(pkt): # Check for odd length
            total += pkt[len(pkt) - 1]
    
        total &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
                            # uses signed ints, but overflow is unlikely in ping)
    
        total = (total >> 16) + (total & 0xffff)    # Add high 16 bits to low 16 bits
        total += (total >> 16)                      # Add carry from above (if any)
    
        return socket.htons((~total) & 0xffff)
    
    def ping(args):
        sock, payload = args[0], args[1]
        header = struct.pack("!BBH", 8, 0, 0)
        checksum = calculate_checksum(header+payload)
        header = struct.pack("!BBH", 8, 0, checksum)
    
        timestamp = time.time()
        sock.send(header+payload)
    
        try:
            response = sock.recv(20+len(payload))
        except socket.timeout:
            return 0
    
        return (len(response), (time.time() - timestamp) * 1000)
    
    
    host = argv[1] # A host that doesn't respond to ping packets > 1500B
    
    # 1 is ICMP protocol number
    sockets = [socket.socket(socket.AF_INET, socket.SOCK_RAW, proto=1) for i in range(12)]
    
    for i, sock in enumerate(sockets):
        sock.settimeout(0.1)
        sock.bind(("0.0.0.0", i))
        sock.connect((host, 1)) # Port number should never matter for ICMP
    
    args = [(sockets[i], bytes(2**i)) for i in range(12)]
    
    for arg in args:
        print(ping(arg))
        arg[0].close()
    

    这实际上向我展示了更令人不安的事情——似乎rtt实际上 减少 随着数据包大小的增加!调用此程序(作为根用户,以获取套接字权限)输出:

    0
    0
    (24, 15.784025192260742)
    (28, 0.04601478576660156)
    (28, 0.025033950805664062)
    (28, 0.033855438232421875)
    (28, 0.03528594970703125)
    (28, 0.04887580871582031)
    (28, 0.05316734313964844)
    (28, 0.03790855407714844)
    (28, 0.0209808349609375)
    (28, 0.024080276489257812)
    

    但现在请注意,当我尝试使用 发出砰的声响 :

    user@mycomputer ~/src/connvitals $ time ping -c1 -s2048 $box
    PING <hostname redacted> (<IP address redacted>): 2048 data bytes
    
    --- <hostname redacted> ping statistics ---
    1 packets transmitted, 0 packets received, 100.0% packet loss
    
    real  0m11.018s
    user  0m0.005s
    sys   0m0.008s
    

    数据包不仅会被丢弃,而且需要11秒才能完成!那么,如果我的超时设置为100ms,为什么这个数据包只需0.04ms就能从python脚本中获得“成功”响应呢??

    提前感谢您提供的任何帮助。

    更新:

    我刚刚再次检查,似乎是多个套接字造成了问题,线程似乎与此无关。当我按顺序ping每个套接字,然后立即关闭它时,我会遇到同样的问题。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Max    7 年前

    所有套接字都相同,并且都绑定到同一主机。数据包中没有任何信息让内核知道要去哪个套接字,并且 raw(7) 似乎暗示所有套接字都将接收它们。

    您可能会得到所有线程中的所有响应,这意味着每个线程得到的响应是您预期的12倍。