I am trying to write a program for parallel checking the data rate for packets of various sizes. However, I noticed something strange that the packet size did not seem to affect the transfer time according to my program, while Unix binary ping
could disable some of the packet sizes that I use. I sent 4 packets containing the string testquest, and one that was only 2000 bytes equal to 0. However, when I printed the results, they all contained "testquest" (and were much shorter than 2000 bytes). The only thing I can conclude is that these sockets somehow get the same packet, which explains how they all have the same rtt.
I did this MCVE to illustrate the problem (you can ignore the "checksum" function, it is included for completeness, but I know from experience that it works):
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
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
if countTo < len(pkt):
total += pkt[len(pkt) - 1]
total &= 0xffffffff
total = (total >> 16) + (total & 0xffff)
total += (total >> 16)
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]
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))
args = [(sockets[i], bytes(2**i)) for i in range(12)]
for arg in args:
print(ping(arg))
arg[0].close()
This actually shows me something more disturbing - it looks like rtt really decreases with increasing packet size! Calling this program (as root, to obtain socket permissions):
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)
but now notice what happens when I try to send a 2048 packet with ping
:
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
The package fell not only, but it takes 11 seconds! So why - if my timeout is set to 100 ms - does this package receive a "successful" response from my python script in just ~ 0.04ms ??
Thanks in advance for any help you can provide.
Update:
, , , , , , . , , - .