计算机网络第二章:作业 2: UDP ping 程序

套接字编程作业 2:UDP Ping 程序

问题描述:

在这个实验中,你将通过Python使用UDP套接字编程,编写一个ping程序。Ping程序是用来测试网络延迟的工具,客户端通过发送请求到服务器,然后计算往返时间(RTT),并处理可能的丢包。作业的主要任务是:

  1. 编写一个UDP客户端,向服务器发送10个ping请求。
  2. 服务器会根据收到的请求返回响应,但30%的请求会被随机丢弃以模拟丢包。
  3. 客户端应处理超时情况,超时阈值为1秒。
  4. 每次ping请求后,客户端应输出收到的响应或超时消息,并显示往返时间(RTT)。

你需要实现的客户端程序功能类似于标准的ping命令,但使用的是UDP协议,而不是ICMP协议。

问题解决流程:

  1. 理解UDP协议的特点

    • UDP是一种无连接、轻量级但不可靠的传输协议,数据包可能会丢失,不保证数据的到达顺序。
    • 在Ping程序中,我们使用UDP,因为其简单且能模拟不可靠的网络通信。
  2. 服务器端实现

    • 我们提供了一个服务器端程序UDPPingerServer.py,它接收来自客户端的请求,随机丢弃30%的请求,剩下的请求则返回给客户端。
    • 你需要在本地或远程服务器上运行该服务器程序,然后实现客户端程序与其通信。
  3. 客户端功能要求

    • 使用UDP向服务器发送10次ping请求,每次请求中包含当前的发送时间。
    • 服务器返回响应后,计算该请求的往返时间(RTT)。
    • 如果请求超过1秒未收到响应,则显示"Request timed out"。
    • 最终输出每次ping的RTT或者超时消息。

步骤 1:服务器代码

服务器代码已经提供,先确保它在本地或远程服务器上运行。

python 复制代码
# UDPPingerServer.py 
from socket import * 
import random

# 创建一个UDP套接字
serverSocket = socket(AF_INET, SOCK_DGRAM)
# 绑定服务器的IP地址和端口号
serverSocket.bind(('', 12000))

while True:
    # 生成一个随机数,用于模拟丢包
    rand = random.randint(0, 10)
    # 接收客户端消息和地址
    message, address = serverSocket.recvfrom(1024)
    # 将收到的消息转换为大写
    message = message.upper()
    # 如果随机数小于4,模拟丢包,不返回任何消息
    if rand < 4:
        continue
    # 否则,将消息返回给客户端
    serverSocket.sendto(message, address)
  • 该服务器程序使用UDP协议监听端口12000,并随机丢弃30%的客户端请求。

步骤 2:客户端代码

我们现在需要编写客户端程序,它将向服务器发送10个ping请求,并显示每次请求的结果,包括往返时间或超时。

python 复制代码
import time
from socket import *

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

# 服务器的地址和端口
serverName = 'localhost'  # 本地运行时使用 'localhost'
serverPort = 12000

# 发送10次ping请求
for sequence_number in range(1, 11):
    # 获取当前时间戳
    sendTime = time.time()
    # 构造ping消息
    message = f'Ping {sequence_number} {sendTime}'
    
    try:
        # 向服务器发送消息
        clientSocket.sendto(message.encode(), (serverName, serverPort))
        
        # 等待服务器响应,超时1秒
        response, serverAddress = clientSocket.recvfrom(1024)
        
        # 计算RTT
        rtt = time.time() - sendTime
        # 打印服务器返回的消息和RTT
        print(f'Received from server: {response.decode()} RTT = {rtt:.4f} seconds')
    
    except timeout:
        # 超时处理
        print('Request timed out')

# 关闭套接字
clientSocket.close()

代码解析:

  1. 创建UDP套接字clientSocket = socket(AF_INET, SOCK_DGRAM) 使用UDP协议,适合无连接的通信。
  2. 设置超时clientSocket.settimeout(1) 设置接收数据包的超时时间为1秒,如果在1秒内未收到响应,则抛出timeout异常。
  3. 发送ping消息clientSocket.sendto(message.encode(), (serverName, serverPort)) 向服务器发送包含序列号和时间戳的ping消息。
  4. 接收服务器响应clientSocket.recvfrom(1024) 接收服务器返回的消息,并计算消息的往返时间(RTT)。
  5. 超时处理:如果未收到响应,客户端会输出"Request timed out"。

步骤 3:运行程序

1. 运行服务器:

在本地或远程服务器上运行提供的服务器代码:

bash 复制代码
python UDPPingerServer.py
2. 运行客户端:

在另一个终端窗口中运行客户端代码:

bash 复制代码
python UDPPingerClient.py

你会看到客户端向服务器发送10次ping请求的输出,类似于以下结果:

Received from server: PING 1 1663261625.64255 RTT = 0.0021 seconds
Received from server: PING 2 1663261626.64305 RTT = 0.0020 seconds
Request timed out
Received from server: PING 4 1663261628.64400 RTT = 0.0021 seconds
...
  • 如果服务器丢弃了请求,客户端会显示Request timed out
  • 如果服务器返回了响应,客户端会显示往返时间(RTT)。

可选练习1

客户端程序需要在发送10次Ping请求后计算最小、最大和平均RTT,同时计算丢包率。我们可以通过保存每次Ping的RTT值,最后计算这些统计数据。具体步骤如下:

实现步骤

  1. 存储每次Ping的RTT:为RTT创建一个列表,每次接收到响应时,将RTT添加到该列表中。
  2. 统计丢包情况:为超时次数创建一个计数器,每次超时时递增。
  3. 计算最小、最大和平均RTT:在发送完所有Ping请求后,通过列表计算最小值、最大值和平均值。
  4. 计算丢包率:丢包率 = (丢失的包数 / 总包数)× 100%。

修改后的代码

python 复制代码
import time
from socket import *

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

# 服务器的地址和端口
serverName = 'localhost'  # 本地运行时使用 'localhost'
serverPort = 12000

# 初始化变量
rtt_list = []  # 存储每次成功Ping的RTT
timeout_count = 0  # 记录超时的次数
total_pings = 10  # 发送的Ping总次数

# 发送10次ping请求
for sequence_number in range(1, total_pings + 1):
    # 获取当前时间戳
    sendTime = time.time()
    # 构造ping消息
    message = f'Ping {sequence_number} {sendTime}'
    
    try:
        # 向服务器发送消息
        clientSocket.sendto(message.encode(), (serverName, serverPort))
        
        # 等待服务器响应,超时1秒
        response, serverAddress = clientSocket.recvfrom(1024)
        
        # 计算RTT
        rtt = time.time() - sendTime
        rtt_list.append(rtt)  # 将RTT加入列表
        
        # 打印服务器返回的消息和RTT
        print(f'Received from server: {response.decode()} RTT = {rtt:.4f} seconds')
    
    except timeout:
        # 超时处理,计数器增加
        print('Request timed out')
        timeout_count += 1

# 关闭套接字
clientSocket.close()

# 如果有成功的Ping,则计算最小、最大和平均RTT
if rtt_list:
    min_rtt = min(rtt_list)
    max_rtt = max(rtt_list)
    avg_rtt = sum(rtt_list) / len(rtt_list)
    
    print(f'\n--- Ping statistics ---')
    print(f'{total_pings} packets transmitted, {len(rtt_list)} packets received, '
          f'{timeout_count / total_pings * 100:.1f}% packet loss')
    print(f'rtt min/avg/max = {min_rtt:.4f}/{avg_rtt:.4f}/{max_rtt:.4f} seconds')

else:
    print(f'\n--- Ping statistics ---')
    print(f'{total_pings} packets transmitted, 0 packets received, 100.0% packet loss')

代码解析

  1. rtt_list:这是一个列表,用于存储每次成功的RTT值。当接收到响应时,将RTT值添加到该列表中。
  2. timeout_count:这是一个计数器,用于记录超时(丢包)的次数。每当请求超时时,计数器会加1。
  3. RTT计算 :程序结束后,检查rtt_list是否为空。如果有接收到的Ping响应,则计算最小、最大和平均RTT,并打印出来。
  4. 丢包率计算 :丢包率通过timeout_count / total_pings * 100计算,表示为百分比形式。

输出示例

假设服务器响应如下:

Received from server: PING 1 1663261625.64255 RTT = 0.0021 seconds
Request timed out
Received from server: PING 3 1663261626.64305 RTT = 0.0020 seconds
Request timed out
Received from server: PING 5 1663261627.64400 RTT = 0.0021 seconds
...

--- Ping statistics ---
10 packets transmitted, 6 packets received, 40.0% packet loss
rtt min/avg/max = 0.0020/0.0021/0.0022 seconds

通过这些修改,客户端程序在完成所有Ping操作后,会输出最小、最大和平均RTT,并计算出丢包率。这种行为模拟了标准ping命令的功能,提供了更加详细的网络通信统计信息。

相关推荐
爱码小白几秒前
网络编程(王铭东老师)笔记
服务器·网络·笔记
zzzhpzhpzzz9 分钟前
Ubuntu如何查看硬件型号
linux·运维·ubuntu
蜜獾云11 分钟前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
陌北v113 分钟前
Docker Compose 配置指南
运维·docker·容器·docker-compose
背着黄油面包的猫27 分钟前
计算机网络基础知识
计算机网络
只会copy的搬运工34 分钟前
Jenkins 持续集成部署——Jenkins实战与运维(1)
运维·ci/cd·jenkins
o(╥﹏╥)44 分钟前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
娶不到胡一菲的汪大东1 小时前
Ubuntu概述
linux·运维·ubuntu
阿里嘎多学长1 小时前
docker怎么部署高斯数据库
运维·数据库·docker·容器
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端