%# -*- 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}

标准的ping使用ICMP，该实验创建一个简单的，
非标准的基于UDP的ping程序。用Python编写一个客户ping程序。
该客户将发送一个简单的ping报文，接收一个从服务器往返的对应pong报文，
并确定从该客户发送ping报文到接收pong报文为止的时延（往返时延RTT）。

要求你的ping程序经UDP向目标服务器发送10个ping报文。
对于每个报文，当对应的pong报文返回时，客户端要确认和打印RTT；
要求考虑分组丢失情况，客户端最多等待1秒，超过该时长则打印丢失报文。

\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}所示。

\begin{figure}[!ht]
  \centering
  \includegraphics[width=8cm]{udp-flow}
  \caption{无连接客户/服务器流程图}
  \label{fig:c:socket_udp-flow}
\end{figure}

\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版）}
\end{itemize}

\subsection{实验步骤}
\label{subsec:c:socket:s:udp_procedure}

下面的服务器代码中30\%的客户端数据包被模拟为丢失。
请参考该代码，按照实验任务完成ping客户端代码。

注意在运行客户端程序前，需要先运行服务器端代码。

使用客服端ping程序经UDP向目标服务器发送10个ping报文。要求：

\begin{enumerate}
  \item 使用UDP发送ping消息（注意：因为UDP是无连接协议，不需要建立连接。）；
  \item 如果服务器在1秒内响应，则打印该响应消息；
  计算并打印每个数据包的往返时间RTT（以秒为单位）；
  \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{思考题}
\label{subsec:c:socket:s:udp_rethink}

尝试修改代码计算所有ping结束时报告最小、最大、平均rtt和计算丢包率（百分比）
以符合标准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}
