第16讲实验报告

一.UDP Ping实验

1.实验代码及注释 

其中UDPPinggerServer.py是已经给出的,需要我们实现的是UDPPingger.py 

UDPPingerServer.py

from socket import *
import random

serverSocket = socket(AF_INET, SOCK_DGRAM) #创建UDP套接字
serverSocket.bind(('', 12000))             #绑定本机IP地址和端口号

while True:   #用无限循环来模拟监听客户端发来的数据报
	rand = random.randint(1, 10)  #生成一个[1,10]的随机数
	message, address = serverSocket.recvfrom(1024) #接收客户端消息
	message = message.upper()                      #将数据报内容转换为大写
	if rand < 4:                  #如果随机数是1,2或3,则不做出响应,模拟30%丢包率
		continue
	serverSocket.sendto(message, address)  #否则,服务器向客户端做出响应

UDPPingger.py 

from socket import *
import time

serverName = '192.168.202.1'  #服务器地址设置为本机IP地址,与UDPPinggerServer中一致
serverPort = 12000        #服务器指定端口为12000,与UDPPinggerServer中一致

clientSocket = socket(AF_INET, SOCK_DGRAM) #创建UDP套接字
clientSocket.settimeout(1)                 #设置超时值1秒

for i in range(10):       #发送10条ping消息
    sendTime = time.time()
    message = ('Ping %d %s' % (i+1, sendTime)).encode() #生成数据报,编码为bytes以便发送
    try:
        clientSocket.sendto(message, (serverName, serverPort)) #向服务器发送数据报
        upperedMessage, serverAddress = clientSocket.recvfrom(1024) #接收服务器的响应信息和服务器地址
        RTT = time.time() - sendTime   #计算RTT
        print('Sequence %d: Reply from %s    RTT = %.3fs' %(i+1, serverName, RTT)) #打印成功接收的信息
    except:
        print('Sequence %d: Request timed out.' %(i+1)) #打印丢包信息

2.实验效果 

现在终端运行 UDPPinggerServer.py,监听数据报

再另开一个终端运行 UDPPingger.py 

二.ICMP Ping实验

1.ICMP协议

ICMP协议(Internet Control Message Protocl,因特网控制报文协议)是一种面向无连接的协议,用于传输出错报告控制信息,属于网络层协议,主要用于在和路由器之间传递控制控制信息

ICMP报文结构

一个ICMP报文包括IP报头(至少 20 字节)、ICMP报头(至少 8 字节)和ICMP报文(即 ICMP 报文的数据部分),当IP报头中的协议字段值为 1 时,就说明这是一个ICMP报文

ICMP报头结构

类型(Type):标识ICMP报文的类型,第一类是取值为1-127的差错报文,第二类是取指128以上的信息报文。

代码(Code):它与类型字段一起共同标识了ICMP报文的详细类型

校验和(Checksum):对包括ICMP报文数据部分在内的整个ICMP数据报的校验和,以检验报文在传输过程中是否出现了差错

本实验中涉及到的ICMP报文类型

(1)ping操作中包括了请求ICMP报文(类型字段值为8)和应答ICMP报文(类型字段值为0):

一台主机向一个节点发送一个类型字段值为8的ICMP报文,如果途中没有异常,则目标返回类型字段值为0的ICMP报文,说明这台主机存在

(2)时间戳请求报文(类型字段值为13)和时间戳应答报文(类型字段值为14)用于测试两台主机之间数据报来回一次的传输时间。传输时,主机填充原始时间戳,接受方收到请求后填充接受时间戳后以类型值字段14的报文格式返回,发送方计算这个时间差

2.实验代码及注释

在begin和and之间的是需要我们实现的部分

import socket
import os
import struct
import time
import select

ICMP_ECHO_REQUEST = 8

#生成校验和
def checksum(str):
    csum = 0
    countTo = (len(str) / 2) * 2
    count = 0
    while count < countTo:
        thisVal = str[count + 1] * 256 + str[count]
        csum = csum + thisVal
        csum = csum & 0xffffffff
        count = count + 2
    if countTo < len(str):
        csum = csum + str[len(str) - 1].decode()
        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

#发送一次Ping数据包
#先生成一个校验和为0的虚假的头部,然后计算出虚假头部与数据的校验和,用其生成真正的头部,最后由真正的头部和数据data构成发送的数据包packect
#头部结构:请求类型(8bits),代码8(bits),新校验和(16bits),报文ID(16bits),报文序号Sequence(16bits)
def sendOnePing(mySocket, ID, sequence, destAddr):
    myChecksum = 0
    ########## Begin ##########
    # 生成一个校验和为0的虚拟头部
    # struct.pack() -- 将字符串信息封装成二进制数据
    # ! -- 以大端模式标准对齐
    # B -- python 1字节int类型
    # H -- python 2字节int类型
    # d -- python 8字节float类型
    header = struct.pack("!2B3H", ICMP_ECHO_REQUEST, 0, myChecksum, ID, sequence)
    data = struct.pack("!d", time.time())
    # 计算头部和数据的校验和
    myChecksum = checksum(header + data)
    #用新的校验和生成真正的头部
    header = struct.pack("!2B3H", ICMP_ECHO_REQUEST, 0, myChecksum, ID, sequence)
    #由真正的头部和数据data构成要发送的包
    packet = header + data
    ########## End ##########
    mySocket.sendto(packet, (destAddr, 1)) 

#接收一次Ping的返回消息
def receiveOnePing(mySocket, ID, sequence, destAddr, timeout):
    timeLeft = timeout

    while 1:
        startedSelect = time.time()
        whatReady = select.select([mySocket], [], [], timeLeft)
        howLongInSelect = (time.time() - startedSelect)
        if whatReady[0] == []:  # Timeout
            return None
        timeReceived = time.time()
        #计算并返回报文延迟、TTL及double类型数据长度
        ########## Begin ##########
        #接收报文
        recPacket, addr = mySocket.recvfrom(1024)
        #取出ICMP报文头部(前20字节是IP报头,第21-28字节是ICMP报头)
        header = recPacket[20: 28]
        #解析ICMP头部字段(和sendOnePing中的pack相对应)
        type, code, checksum, packetID, sequence = struct.unpack("!2B3H", header)
        #计算延迟及TTL信息
        if type == 0 and packetID == ID:  # type should be 0
            byte_in_double = struct.calcsize("!d")   #计算数据部分所占的字节数(在本例中,数据中只有发送时间)
            #注意unpack返回值是元组,只pack了一个对象,那么unpack得到的就是一元组
            timeSent = struct.unpack("!d", recPacket[28: 28 + byte_in_double])[0]
            delay = timeReceived - timeSent          #计算数据报传输时延
            # IP报头的第8个字节是TTL(Time To Live)字段,ord函数将字符转换成对应的整数,即用于获取ASCII给定字符的值
            ttl = ord(struct.unpack("!c", recPacket[8:9])[0].decode())
            return (delay, ttl, byte_in_double)
        ########## End ##########
        timeLeft = timeLeft - howLongInSelect
        if timeLeft <= 0:
            return None 

#向指定地址发送Ping消息
def doOnePing(destAddr, ID, sequence, timeout):
    icmp = socket.getprotobyname("icmp")

    ########## Begin ##########
    mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) #创建使用ICMP协议的原始套接字
    ########## End ##########
    
    sendOnePing(mySocket, ID, sequence, destAddr)
    delay = receiveOnePing(mySocket, ID, sequence, destAddr, timeout)

    mySocket.close()
    return delay

#主函数Ping
def ping(host, timeout=1):
    
    # timeout=1指: 如果1秒内没从服务器返回,客户端认为Ping或Pong丢失。
    dest = socket.gethostbyname(host)
    print("Pinging " + dest + " using Python:")
    
    #每秒向服务器发送一次Ping请求
    myID = os.getpid() & 0xFFFF  # 返回进程ID
    loss = 0
    for i in range(4):
        result = doOnePing(dest, myID, i, timeout)
        if not result:
            print("Request timed out.")
            loss += 1
        else:
            delay = int(result[0]*1000)
            ttl = result[1]
            bytes = result[2]
            print("Received from " + dest + ": byte(s)=" + str(bytes) + " delay=" + str(delay) + "ms TTL=" + str(ttl))
        time.sleep(1)  # one second
    print("Packet: sent = " + str(4) + " received = " + str(4-loss) + " lost = " + str(loss))

    return

ping("www.baidu.com")

3.实验效果

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值