|
|
|
@ -6,43 +6,59 @@
|
|
|
|
|
\subsection{实验目的}
|
|
|
|
|
\label{subsec:c:socket:s:udp_object}
|
|
|
|
|
|
|
|
|
|
熟悉Python中UDP套接字编程的基础知识,掌握使用UDP套接字发送和接收数据包,
|
|
|
|
|
熟悉基于Python进行UDP套接字编程的基础知识,掌握使用UDP套接字发送和接收数据包,
|
|
|
|
|
以及设置正确的套接字超时,
|
|
|
|
|
了解Ping应用程序及其在计算数据包丢失率等统计数据方面的有用性。
|
|
|
|
|
了解Ping应用程序的基本概念,并理解其在简单判断网络状态,
|
|
|
|
|
例如计算数据包丢失率等统计数据方面的意义。
|
|
|
|
|
|
|
|
|
|
\subsection{实验内容}
|
|
|
|
|
\label{subsec:c:socket:s:udp_content}
|
|
|
|
|
|
|
|
|
|
标准的ping使用ICMP,该实验创建一个简单的,
|
|
|
|
|
非标准的基于UDP的ping程序。用Python编写一个客户ping程序。
|
|
|
|
|
该客户将发送一个简单的ping报文,接收一个从服务器往返的对应pong报文,
|
|
|
|
|
并确定从该客户发送ping报文到接收pong报文为止的时延(往返时延RTT)。
|
|
|
|
|
|
|
|
|
|
要求你的ping程序经UDP向目标服务器发送10个ping报文。
|
|
|
|
|
对于每个报文,当对应的pong报文返回时,客户端要确认和打印RTT;
|
|
|
|
|
要求考虑分组丢失情况,客户端最多等待1秒,超过该时长则打印丢失报文。
|
|
|
|
|
\begin{enumerate}
|
|
|
|
|
\item 操作系统附带的标准Ping命令使用ICMP进行通信,
|
|
|
|
|
本实验要求学生编程实现一个简单的,非标准的,基于UDP进行通信的Ping程序。
|
|
|
|
|
学生需要用Python编写一个Ping客户端。
|
|
|
|
|
客户端程序发送一个ping报文,然后接收一个从已经提供的服务器上返回的对应pong报文,
|
|
|
|
|
并计算出从该客户发送ping报文到接收到pong报文为止的往返时延
|
|
|
|
|
(Round-Trip Time,RTT)。
|
|
|
|
|
|
|
|
|
|
\item 在客户端程序一次执行过程中,
|
|
|
|
|
学生编写的的Ping客户端程序需经UDP向服务器发送10个ping报文。
|
|
|
|
|
对于每个报文,当对应的pong报文返回时,客户端程序要确认并打印输出RTT值;
|
|
|
|
|
在整个执行过程中,客户端程序需要考虑分组丢失情况,
|
|
|
|
|
客户端最多等待1秒,超过该时长则打印丢失报文。
|
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
|
\subsection{实验原理、方法和手段}
|
|
|
|
|
\label{subsec:c:socket:s:udp_principle}
|
|
|
|
|
|
|
|
|
|
UDP提供了无连接通信,且不对传送数据包进行可靠性保证,
|
|
|
|
|
适合于一次传输少量数据,UDP传输的可靠性由应用层负责。
|
|
|
|
|
常用的UDP端口号有:53(DNS)、69(TFTP)、161(SNMP),
|
|
|
|
|
使用UDP协议包括:TFTP、SNMP、NFS、DNS、BOOTP。
|
|
|
|
|
|
|
|
|
|
UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。
|
|
|
|
|
但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,
|
|
|
|
|
适合对可靠性要求不高的应用程序,
|
|
|
|
|
或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。
|
|
|
|
|
|
|
|
|
|
基于UDP协议的无连接客户/服务器的工作流程是:
|
|
|
|
|
在服务器端首先启动服务器通过调用socket( )创建套接字,
|
|
|
|
|
然后调用bind( )指定服务器socket地址,最后服务器调用recvfrom( )等待接收数据。
|
|
|
|
|
在客户端,客户调用socket( )创建套接字,然后调用sendto( )向服务器发送数据。
|
|
|
|
|
服务器接收到客户发来数据后,调用sendto( )向客户发送应答数据,
|
|
|
|
|
客户调用recvfrom接收服务器发来的应答数据。
|
|
|
|
|
一旦数据传输结束,服务器和客户通过调用close( )来关闭套接字。
|
|
|
|
|
具体流程如图\ref{fig:c:socket_udp-flow}所示。
|
|
|
|
|
UDP作为一种传输层协议,只提供了无连接通信,且不对传送的数据包进行可靠性保证,
|
|
|
|
|
因此只适合于一次传输少量数据的应用场景,
|
|
|
|
|
如果在传输过程中需要保证可靠性,则这种可靠性应该由应用层负责。
|
|
|
|
|
本实验创建的Ping程序正是一种不需要保证可靠性的程序,
|
|
|
|
|
并需要利用这种不可靠性来测量网络的联通情况。
|
|
|
|
|
|
|
|
|
|
虽然UDP不保证通信的可靠性,包到达的顺序,也不提供流量控制。
|
|
|
|
|
但正是因为UDP的控制选项较少,所以在数据传输过程中延迟小、数据传输效率高,
|
|
|
|
|
一些对可靠性要求不高,但对性能等开销更敏感的应用层协议会选择基于UDP进行实现,
|
|
|
|
|
常见的使用UDP的应用层协议包括TFTP、SNMP、NFS、DNS、BOOTP等,
|
|
|
|
|
通常占用53(DNS)、69(TFTP)、161(SNMP)等端口。
|
|
|
|
|
|
|
|
|
|
基于UDP的无连接客户/服务器在Python实现中的工作流程如下:
|
|
|
|
|
|
|
|
|
|
\begin{enumerate}
|
|
|
|
|
\item 首先在服务器端通过调用\texttt{socket()}创建套接字来启动一个服务器;
|
|
|
|
|
\item 服务器调用\texttt{bind()}指定服务器的套接字地址,
|
|
|
|
|
然后调用\texttt{recvfrom()}等待接收数据。
|
|
|
|
|
\item 在客户端调用\texttt{socket()}创建套接字,
|
|
|
|
|
然后调用\texttt{sendto()}向服务器发送数据。
|
|
|
|
|
\item 服务器接收到客户端发来的数据后,调用\texttt{sendto()}向客户发送应答数据,
|
|
|
|
|
\item 客户调用\texttt{recvfrom()}接收服务器发来的应答数据。
|
|
|
|
|
\item 一旦数据传输结束,服务器和客户通过调用\texttt{close()}来关闭套接字。
|
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
|
注意在不同的计算机语言实现中,上述调用的名字和具体工作流程可能略有不同。
|
|
|
|
|
基于Python的UDP程序工作详细流程如图\ref{fig:c:socket_udp-flow}所示。
|
|
|
|
|
|
|
|
|
|
\begin{figure}[!ht]
|
|
|
|
|
\centering
|
|
|
|
@ -51,68 +67,62 @@ UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠
|
|
|
|
|
\label{fig:c:socket_udp-flow}
|
|
|
|
|
\end{figure}
|
|
|
|
|
|
|
|
|
|
基于Python进行UDP消息的接收操作时,Python程序将工作在阻塞状态,
|
|
|
|
|
即未收到数据包时,Python程序将挂起等待而不会继续执行。
|
|
|
|
|
如果程序运行中网络连接出现了问题,导致数据包无法及时到达,
|
|
|
|
|
这种阻塞式的工作模式将会严重的干扰程序的执行。
|
|
|
|
|
为了解决这个问题,Python的套接字通信库提供了一种“超时”机制来防止程序卡死。
|
|
|
|
|
在Python套接字程序中,
|
|
|
|
|
套接字对象提供了一个\texttt{settimeout()}方法来限制\texttt{recvfrom()}函数的等待时间,
|
|
|
|
|
当\texttt{recvfrom()}函数阻塞等待超过这个时间(一般称为“超时时间”)后仍然没有收到数据时,
|
|
|
|
|
程序将会抛出一个异常来说明发生了等待数据接收超时事件。
|
|
|
|
|
在编写Python网络通信程序时,可以利用这个机制来判断是否接收数据超时。
|
|
|
|
|
|
|
|
|
|
\subsection{实验条件}
|
|
|
|
|
\label{subsec:c:socket:s:udp_requirement}
|
|
|
|
|
|
|
|
|
|
\begin{itemize}
|
|
|
|
|
\item 装有python环境的电脑两台;
|
|
|
|
|
\item 局域网环境;
|
|
|
|
|
\item 服务器程序(实验步骤中已给出)。
|
|
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
参考资料:
|
|
|
|
|
|
|
|
|
|
\begin{itemize}
|
|
|
|
|
\item {J.F Kurose and K.W. Ross, 计算机网络自顶向下方法(第7版)}
|
|
|
|
|
\item 服务器程序(\nameref{subsec:c:socket:s:udp_additional}中已给出);
|
|
|
|
|
\item Python语言参考手册 -- UDP部分\footnote{可以参考Python3官方手册的
|
|
|
|
|
\href{https://docs.python.org/zh-cn/3/library/socket.html}
|
|
|
|
|
{套接字}部分,也可以查询其他相关手册}。
|
|
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
|
|
\subsection{实验步骤}
|
|
|
|
|
\label{subsec:c:socket:s:udp_procedure}
|
|
|
|
|
|
|
|
|
|
下面的服务器代码中30\%的客户端数据包被模拟为丢失。
|
|
|
|
|
请参考该代码,按照实验任务完成ping客户端代码。
|
|
|
|
|
本实验\nameref{subsec:c:socket:s:udp_additional}一节中展示了一段Python代码,
|
|
|
|
|
实现了一个UDP服务器,该服务器还会模拟丢失30\%的客户端数据包。
|
|
|
|
|
请参考该代码,基于Python按照实验任务要求完成Ping程序的客户端。
|
|
|
|
|
|
|
|
|
|
注意在运行客户端程序前,需要先运行服务器端代码。
|
|
|
|
|
注意:在运行客户端程序前,需要先运行服务器端代码。
|
|
|
|
|
|
|
|
|
|
使用客服端ping程序经UDP向目标服务器发送10个ping报文。要求:
|
|
|
|
|
编写成功后,使用客服端Ping程序经UDP向目标服务器发送10个ping报文。要求:
|
|
|
|
|
|
|
|
|
|
\begin{enumerate}
|
|
|
|
|
\item 使用UDP发送ping消息(注意:因为UDP是无连接协议,不需要建立连接。);
|
|
|
|
|
\item 如果服务器在1秒内响应,则打印该响应消息;
|
|
|
|
|
计算并打印每个数据包的往返时间RTT(以秒为单位);
|
|
|
|
|
\item 否则,打印“请求超时”。
|
|
|
|
|
\item 否则,打印“请求超时”(中英文皆可)。
|
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
|
在开发过程中,可以将客户端程序和服务器程序放在同一台电脑上进行测试。
|
|
|
|
|
在完成代码调试后,可以尝试将客户端和服务器代码运行在不同网络环境,
|
|
|
|
|
记录并分析结果。下面是UDP服务器示例代码:
|
|
|
|
|
记录并分析结果。
|
|
|
|
|
|
|
|
|
|
\begin{code}[python]
|
|
|
|
|
from socket import *
|
|
|
|
|
# 创建一个UDP套接字(SOCK_DGRAM)
|
|
|
|
|
serverSocket = socket(AF_INET, SOCK_DGRAM)
|
|
|
|
|
serverSocket.bind(('', 12000))
|
|
|
|
|
while True:
|
|
|
|
|
rand = random.randint(0, 10)
|
|
|
|
|
message, address = serverSocket.recvfrom(1024)
|
|
|
|
|
message = message.upper()
|
|
|
|
|
# 模拟40%的数据包丢失。
|
|
|
|
|
if rand < 4:
|
|
|
|
|
Continue
|
|
|
|
|
serverSocket.sendto(message, address)
|
|
|
|
|
\end{code}
|
|
|
|
|
|
|
|
|
|
\subsection{思考题}
|
|
|
|
|
\subsection{进阶任务}
|
|
|
|
|
\label{subsec:c:socket:s:udp_rethink}
|
|
|
|
|
|
|
|
|
|
尝试修改代码计算所有ping结束时报告最小、最大、平均rtt和计算丢包率(百分比)
|
|
|
|
|
以符合标准ping程序的工作方式。
|
|
|
|
|
尝试修改代码,在程序运行结束时,计算所有ping消息的最小、最大和平均RTT,
|
|
|
|
|
并计算丢包率(丢失数据包在总数据包中所占有的百分比)。
|
|
|
|
|
即构造出一个符合标准Windows版Ping程序工作模式的基于UDP版Ping程序。
|
|
|
|
|
|
|
|
|
|
\subsection{考核方法}
|
|
|
|
|
\label{subsec:c:socket:s:udp_criterion}
|
|
|
|
|
|
|
|
|
|
本次实验需提交一份实验报告和代码文件。报告内容应当包括以下三个部分:
|
|
|
|
|
本实验需提交一份实验报告和编写的代码文件。报告内容应当包括以下三个部分:
|
|
|
|
|
\begin{itemize}
|
|
|
|
|
\item 代码的说明;
|
|
|
|
|
\item 不同环境下代码运行的结果;
|
|
|
|
@ -128,3 +138,23 @@ UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠
|
|
|
|
|
\item 实验报告中详细记录了实验过程,在实验中所遇到的问题以及解决方法,20分;
|
|
|
|
|
\item 实验报告中仔细分析了实验结果,并能提出自己的改进措施,20分。
|
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
|
\subsection{附件}
|
|
|
|
|
\label{subsec:c:socket:s:udp_additional}
|
|
|
|
|
|
|
|
|
|
基于Python的UDP套接字示例程序:
|
|
|
|
|
|
|
|
|
|
\begin{code}[python]
|
|
|
|
|
from socket import *
|
|
|
|
|
# 创建一个UDP套接字(SOCK_DGRAM)
|
|
|
|
|
serverSocket = socket(AF_INET, SOCK_DGRAM)
|
|
|
|
|
serverSocket.bind(('', 12000))
|
|
|
|
|
while True:
|
|
|
|
|
rand = random.randint(0, 10)
|
|
|
|
|
message, address = serverSocket.recvfrom(1024)
|
|
|
|
|
message = message.upper()
|
|
|
|
|
# 模拟30%的数据包丢失。
|
|
|
|
|
if rand < 4:
|
|
|
|
|
Continue
|
|
|
|
|
serverSocket.sendto(message, address)
|
|
|
|
|
\end{code}
|
|
|
|
|