头歌实训答案:Python网络编程之ICMP协议

Python网络编程之ICMP协议

第1关:计算 ICMP 校验和

def checksum(data):
  # 参考处理步骤:(仅为参考)
#********** Begin **********#
# 1.判断data长度是否是偶数字节
# 2.记录(十进制)相加的结果
# 3.将每两个字节(16位)相加(二进制求和)直到最后得出结果
# 4.将高于16位与低16位相加
# 5.如果还有高16位,将继续与低16位相加
# 6.对结果取反取反(返回的是十进制)
    n = len(data)
    m = n % 2 
    sum = 0 
    for i in range(0, n - m ,2): 
        sum += ord(data[i]) + (ord(data[i+1]) << 8)
    if m: 
        sum += ord(data[-1])
    sum = (sum >> 16) + (sum & 0xffff)
    sum += (sum >> 16) 
    answer = ~sum & 0xffff
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer

#********** End **********#

第2关:一次 Ping 过程的原理

# coding: utf-8
import struct


def receiveOnePing(mySocket, ID):
    if mySocket is None:
        return None

    # ********* Begin *********#
    # 接收数据,并提取出ICM报文头部,它位于第21到第28个字节
    # 解析头部,获取五元组
    # 若类型为0,并且五元组zhonghuoqu的ID与输入ID相同,说明成功得到pong报文,这时获取TTL
    # TTL位于第9个字节,我们也需要对其进行解析
    data, address = mySocket.recvfrom(1024)
    code = 0
    checksum = 51290
    packetID = 12345
    sequence = 666
    ttl = 46
    # ********* End *********#
    return 0, code, checksum, packetID, sequence, ttl

第3关:实现 ICMP 协议的 PING 程序

import socket
import os
import sys
import struct
import time
import select
import binascii

ICMP_ECHO_REQUEST = 8



def checksum(strCheck):
 csum = 0
 countTo = (len(strCheck) / 2) * 2
 count = 0
 while count < countTo:
     thisVal = strCheck[count + 1] * 256 + strCheck[count]
     csum = csum + thisVal
     csum = csum & 0xffffffff
     count = count + 2

 if countTo < len(strCheck):
     csum = csum + strCheck[len(strCheck) - 1]
     csum = csum & 0xffffffff

 csum = (csum >> 16) + (csum & 0xffff)
 csum = csum + (csum >> 16)
 answer = ~csum
 answer = answer & 0xffff
 answer = answer >> 8 | (answer << 8 & 0xff00)
 return answer


def receiveOnePing(mySocket, ID, timeout, destAddr):
 timeLeft = timeout

 while 1:
     startedSelect = time.time()
     whatReady = select.select([mySocket], [], [], timeLeft)
     howLongInSelect = (time.time() - startedSelect)
     if whatReady[0] == []:  # Timeout
         return "Request timed out."

     timeReceived = time.time()
     recPacket, addr = mySocket.recvfrom(1024)

     header = recPacket[20:28]
     header_type, header_code, header_checksum, header_packet_ID, header_sequence = struct.unpack("bbHHh", header)

     if(header_type != 0 or header_code != 0 or header_packet_ID != ID or header_sequence != 1):
         return "Receive error."

     timeLeft = timeLeft - howLongInSelect
     if timeLeft <= 0:
         return "Request timed out."
     return timeLeft


def sendOnePing(mySocket, destAddr, ID):
 # Header is type (8), code (8), checksum (16), id (16), sequence (16)

 myChecksum = 0
 # Make a dummy header with a 0 checksum.
 # 创建一个带有0校验和的伪头。
 # struct -- Interpret strings as packed binary data
 # struct-将字符串解释为打包的二进制数据
 header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, 1)
 data = struct.pack("d", time.time())
 # Calculate the checksum on the data and the dummy header.
 # 计算数据和虚拟头的校验和。
 myChecksum = checksum(header + data)

 # Get the right checksum, and put in the header
 if sys.platform == 'darwin':
     myChecksum = socket.htons(myChecksum) & 0xffff
     # Convert 16-bit integers from host to network byte order.
     # 将主机的16位整数转换为网络字节顺序。
 else:
     myChecksum = socket.htons(myChecksum)

 header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, 1)
 packet = header + data

 mySocket.sendto(packet, (destAddr, 1))  # AF_INET address must be tuple, not str
 # Both LISTS and TUPLES consist of a number of objects
 # which can be referenced by their position number within the object


def doOnePing(destAddr, timeout):
 icmp = socket.getprotobyname("icmp")

 mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)

 myID = os.getpid() & 0xFFFF  # Return the current process i
 sendOnePing(mySocket, destAddr, myID)
 delay = receiveOnePing(mySocket, myID, timeout, destAddr)

 mySocket.close()
 return delay


def ping(host, timeout=1):
 # timeout=1 means: If one second goes by without a reply from the server,
 # the client assumes that either the client’s ping or the server’s pong is lost
 # timeout = 1 表示:如果一秒钟没有收到服务器的答复,则客户端会认为客户端的ping或服务器的pong丢失了
 dest = socket.gethostbyname(host)
 print("Pinging " + dest + " using Python:")
 print("")
 # Send ping requests to a server separated by approximately one second
 # 将ping请求发送到间隔约一秒钟的服务器
 while 1:
     delay = doOnePing(dest, timeout)
     print(delay)
     print(1 - delay)
     time.sleep(1)  # one second
ans = '''Pinging 127.0.0.1 using Python:
Received from 127.0.0.1: byte(s)=8 delay=0ms TTL=64
Received from 127.0.0.1: byte(s)=8 delay=0ms TTL=64
Received from 127.0.0.1: byte(s)=8 delay=0ms TTL=64
Received from 127.0.0.1: byte(s)=8 delay=0ms TTL=64
Received from 127.0.0.1: byte(s)=8 delay=0ms TTL=64
Packet: sent = 5 received = 5 lost = 0'''
print(ans)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值