You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
p2l5wexnu/data/ch_socket/sec_udp.tex

161 lines
7.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

%# -*- coding: utf-8-unix -*-
\section{套接字基础与UDP通信}
\label{sec:c:socket:s:udp}
\subsection{实验目的}
\label{subsec:c:socket:s:udp_object}
熟悉基于Python进行UDP套接字编程的基础知识掌握使用UDP套接字发送和接收数据包
以及设置正确的套接字超时,
了解Ping应用程序的基本概念并理解其在简单判断网络状态
例如计算数据包丢失率等统计数据方面的意义。
\subsection{实验内容}
\label{subsec:c:socket:s:udp_content}
\begin{enumerate}
\item 操作系统附带的标准Ping命令使用ICMP进行通信
本实验要求学生编程实现一个简单的非标准的基于UDP进行通信的Ping程序。
学生需要用Python编写一个Ping客户端。
客户端程序发送一个ping报文然后接收一个从已经提供的服务器上返回的对应pong报文
并计算出从该客户发送ping报文到接收到pong报文为止的往返时延
Round-Trip TimeRTT
\item 在客户端程序一次执行过程中,
学生编写的的Ping客户端程序需经UDP向服务器发送10个ping报文。
对于每个报文当对应的pong报文返回时客户端程序要确认并打印输出RTT值
在整个执行过程中,客户端程序需要考虑分组丢失情况,
客户端最多等待1秒超过该时长则打印丢失报文。
\end{enumerate}
\subsection{实验原理、方法和手段}
\label{subsec:c:socket:s:udp_principle}
UDP作为一种传输层协议只提供了无连接通信且不对传送的数据包进行可靠性保证
因此只适合于一次传输少量数据的应用场景,
如果在传输过程中需要保证可靠性,则这种可靠性应该由应用层负责。
本实验创建的Ping程序正是一种不需要保证可靠性的程序
并需要利用这种不可靠性来测量网络的联通情况。
虽然UDP不保证通信的可靠性包到达的顺序也不提供流量控制。
但正是因为UDP的控制选项较少所以在数据传输过程中延迟小、数据传输效率高
一些对可靠性要求不高但对性能等开销更敏感的应用层协议会选择基于UDP进行实现
常见的使用UDP的应用层协议包括TFTP、SNMP、NFS、DNS、BOOTP等
通常占用53DNS、69TFTP、161SNMP等端口。
基于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
\includegraphics[width=8cm]{udp-flow}
\caption{无连接客户/服务器流程图}
\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 服务器程序(\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}
本实验\nameref{subsec:c:socket:s:udp_additional}一节中展示了一段Python代码
实现了一个UDP服务器该服务器还会模拟丢失30\%的客户端数据包。
请参考该代码基于Python按照实验任务要求完成Ping程序的客户端。
注意:在运行客户端程序前,需要先运行服务器端代码。
编写成功后使用客服端Ping程序经UDP向目标服务器发送10个ping报文。要求
\begin{enumerate}
\item 使用UDP发送ping消息注意因为UDP是无连接协议不需要建立连接。
\item 如果服务器在1秒内响应则打印该响应消息
计算并打印每个数据包的往返时间RTT以秒为单位
\item 否则,打印“请求超时”(中英文皆可)。
\end{enumerate}
在开发过程中,可以将客户端程序和服务器程序放在同一台电脑上进行测试。
在完成代码调试后,可以尝试将客户端和服务器代码运行在不同网络环境,
记录并分析结果。
\subsection{进阶任务}
\label{subsec:c:socket:s:udp_rethink}
尝试修改代码在程序运行结束时计算所有ping消息的最小、最大和平均RTT
并计算丢包率(丢失数据包在总数据包中所占有的百分比)。
即构造出一个符合标准Windows版Ping程序工作模式的基于UDP版Ping程序。
\subsection{考核方法}
\label{subsec:c:socket:s:udp_criterion}
本实验需提交一份实验报告和编写的代码文件。报告内容应当包括以下三个部分:
\begin{itemize}
\item 代码的说明;
\item 不同环境下代码运行的结果;
\item 对结果的分析和总结体会。
\end{itemize}
本实验评分标准:
\begin{enumerate}
\item 规定时间内完成实验报告20分
\item 代码正确运行20分不能正常运行0分
\item 实验报告格式整洁20分
\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}