%# -*- coding: utf-8-unix -*-

\section{SMTP客户端实现}
\label{sec:c:socket:s:smtp}

\subsection{实验目的}
\label{subsec:c:socket:s:smtp_object}

进一步理解和掌握Python中TCP套接字编程的基础知识，
理解SMTP报文格式，了解开发一个简单应用程序的流程。

\subsection{实验内容}
\label{subsec:c:socket:s:smtp_content}

创建一个可以向任何接收方发送电子邮件的简单邮件客户端。
通过Python编写代码与邮件服务器创建一个TCP连接，
使用SMTP协议与邮件服务器交谈并发送邮件报文，最后关闭连接。

\subsection{实验原理、方法和手段}
\label{subsec:c:socket:s:smtp_principle}

SMTP（Simple Mail Transfer Protocol）即简单邮件传输协议， 
是因特网电子邮件中主要的应用层协议，它使用TCP可靠数据传输服务，
从发送方的邮件服务器向接收方的邮件服务器发送邮件。
SMTP有两个部分：运行在发送方邮件服务器的客户端和运行在接收方邮件服务器的服务器端。

RFC5321给出了SMTP的定义。SMTP有众多出色的性质，
但也具有一些陈旧特征，
例如，它限制所有邮件报文的主体部分只能采用简单的7比特ASCII码表示。
所以在用SMTP传送邮件之前需要将二进制多媒体数据编码为ASCII码，
在传输后再进行解码还原为多媒体数据。 

下来分析一个在SMTP客户（C）和SMTP服务器（S）之间交换报文文本的例子。
客户的主机名为crepes.fr，服务器的主机名为hamburger.edu。
以C：开头的ASCI码文本行正是客户交给其TCP套接字的那些行，
以S：开头的ASCl码则是服务器发送给其TCP套接字的那些行。
一旦创建了TCP连接，就开始了如下过程:

\begin{code}[text]
  S: 220 hamburger. edu 
  C: HELO crepes. fr 
  S: 250 Hello crepes. fr, pleased to meet you 
  C: MAIL FROM:<alice@ crepes. fr>
  S: 250 alice@ crepes. fr... Sender ok 
  C: RCPT TO:<bobehamburger. edu>
  S: 250 bob@ hamburger. edu... Recipient ok 
  C: DATA S:354 Enter mail, end with "."on a line by itself 
  C: Do you like ketchup?
  C: How about pickles?
  C: .
  S: 250 Message accepted for delivery 
  C: QUIT 
  S: 221 hamburger. edu closing connection
\end{code}

在上例中，客户从邮件服务器crepes.fr向邮件服务器hamburger.edu发送了一个报文
（"Do you like ketchup？How about pickles？"）。
作为对话的一部分，
该客户发送了5条命令：HELO（是HELLO的缩写）、MAIL FROM、RCPTTO、DATA以及QUIT。
这些命令都是自解释的。
该客户通过发送一个只包含一个句点的行，向服务器指示该报文结束了。
（按照ASClI码的表示方法，每个报文以CRLF.CRLF结束，其中的CR和LF分别表示回车和换行。）
服务器对每条命令做出回答，其中每个回答含有一个回答码和一些（可选的）英文解释。
我们在这里指出SMTP用的是持续连接：
如果发送邮件服务器有几个报文发往同一个接收邮件服务器，
它可以通过同一个TCP连接发送这些所有的报文。
对每个报文，该客户用一个新的MAIL FROM:crepes.fr开始，
用一个独立的句点指示该邮件的结束，并且仅当所有邮件发送完后才发送QUIT。

\subsection{实验条件}
\label{subsec:c:socket:s:smtp_requirement}

\begin{itemize}
  \item 下列装有python环境的电脑一台；
  \item 邮件服务器账号（开启SMTP）；
  \item 部分代码（实验步骤中已给出）。
\end{itemize}

参考资料：

\begin{itemize}
  \item {J.F Kurose and K.W. Ross, 计算机网络自顶向下方法（第7版）}
\end{itemize}

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

通过Python开发一个简单的SMTP客户端发送邮件。
补充完善下面的代码并通过向不同的账号发送电子邮件测试程序。

\begin{code}[python]
  from socket import * 
  msg = "\r\n I love computer networks!"
  endmsg = "\r\n.\r\n"
  # 选择一个邮件服务器 
  mailserver = #按需补充
  # 创建socket和邮件服务器建立TCP连接
  recv = clientSocket.recv(1024)
  print(recv)
  if recv[:3] != '220':
      print '220 reply not received from server.'
  # 发送HELO命令，打印服务器响应。
      heloCommand = 'HELO Alice\r\n'
      clientSocket.send(heloCommand)
      recv1 = clientSocket.recv(1024)
      print((recv1)
  
  if recv1[:3] != '250':
  print ('250 reply not received from server.')
  
  # 发送MAIL FROM命令，打印服务器响应。
  # 发送RCPT TO命令，打印服务器响应。
  # 发送DATA命令，打印服务器响应。
  # 发送邮件内容。
  # 消息以单个"."结束。
  # 发送QUIT命令，获取服务器响应。
\end{code}

\subsection{思考题}
\label{subsec:c:socket:s:smtp_rethink}

修改代码使程序发送的邮件中不仅包含文本还能够包含图片。

\subsection{注意事项及有关说明}
\label{subsec:c:socket:s:smtp_notice}

注意部分邮件服务器默认关闭SMTP，需进入设置手动开启SMTP协议。

\subsection{考核方法}
\label{subsec:c:socket:s:smtp_criterion}

同实验\ref{sec:c:socket:s:udp}。
