我正在尝试在Python中实现ping服务器,并且正在查看Pyping的源代码作为参考:https://github.com/Akhavi/pyping/blob/master/pyping/core.py
我无法理解为计算ICMP回显请求的校验和而实现的 calculate_checksum 功能。它的实现方式如下:
def calculate_checksum(source_string):
countTo = (int(len(source_string) / 2)) * 2
sum = 0
count = 0
# Handle bytes in pairs (decoding as short ints)
loByte = 0
hiByte = 0
while count < countTo:
if (sys.byteorder == "little"):
loByte = source_string[count]
hiByte = source_string[count + 1]
else:
loByte = source_string[count + 1]
hiByte = source_string[count]
sum = sum + (ord(hiByte) * 256 + ord(loByte))
count += 2
# Handle last byte if applicable (odd-number of bytes)
# Endianness should be irrelevant in this case
if countTo < len(source_string): # Check for odd length
loByte = source_string[len(source_string) - 1]
sum += ord(loByte)
sum &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
# uses signed ints, but overflow is unlikely in ping)
sum = (sum >> 16) + (sum & 0xffff) # Add high 16 bits to low 16 bits
sum += (sum >> 16) # Add carry from above (if any)
answer = ~sum & 0xffff # Invert and truncate to 16 bits
answer = socket.htons(answer)
return answer
总和&= 0xffffffff 用于将总和截断为32位。但是,多余的位(第33位)发生了什么。难道不应该将其作为进位加到总和上吗?另外,在此之后,我将无法理解代码。
我阅读了RFC1071文档(http://www.faqs.org/rfcs/rfc1071.html),该文档解释了如何实现校验和,但是我对此了解不多。
任何帮助将不胜感激。谢谢!
答案 0 :(得分:0)
我终于能够弄清 calculate_checksum 函数的工作原理,并且我尝试在下面进行解释。
校验和计算如下(根据RFC1071):
source_string中的相邻八位字节配对以形成16位 整数,而这些整数的1's complement sum为 计算。 如果八位位组的数目为奇数,则在n-1个八位位组中创建对,然后将其相加,然后将剩余的八位位组添加到总和中。
结果和被截断为16位(要注意进位),并通过取1的补码来计算校验和。最终的校验和应为16位长。
让我们举个例子。
如果要对八位字节[A,B,C,D,E]序列计算校验和,则创建的对将为[A,B]和[C,D],其余八位字节为E。对[a,b]可以计算如下:
a * 256 + b ,其中a和b是八位位组
假设a为11001010,b为00010001,则a * 256 + b = 1100101000010001,从而得出八位位组的连接结果。
1的补码和计算如下:
sum = [A + B] +'[C + D] +'E,其中+'代表1的补码 加
现在回到代码,在 sum&= 0xffffffff 行之前的所有内容都会计算出我们之前计算出的1的补码和。
总和&= 0xffffffff
用于将总和截断为32位,尽管由于source_string的大小不是很大,所以ping中不大可能超过总和
(source_string =标头(8个字节)+有效载荷(可变长度))。
总和=(总和>> 16)+(总和&0xffff)
这段代码是针对总和大于16位的情况实现的。总和分为两部分:
(sum >> 16):高阶16位
(sum&0xffff):低阶16位
,然后添加这两个部分。最终结果可能是比16位多16位
总和+ =(总和>> 16)
此行用于上一次计算所得的总和长于16位的情况,并用于处理进位,类似于上一行。
最后,将计算1的补码并将其截断为16位。 socket.htons()函数用于根据设备的体系结构(小字节序和大字节序)维护发送到网络的字节排列。