1.系统功能设计
创建一个向任何接收方发送电子邮件的简单邮件客户。你的客户将必须与邮件服务器(如谷歌的电子邮件服务器)创建一个TCP连接,使用SMTP协议与该邮件服务器进行交谈,经该邮件服务器向某接收方(如你的朋友)发送一个电子邮件报文,最后关闭与该邮件服务器的TCP连接。
2.系统功能流程图
3.系统功能实现测试
本系统实现一个SMTP客户端,使用QQ邮箱作为发件人,向指定的QQ邮箱发送一封邮件。SMTP协议即简单邮件传输协议,允许用户按照标准发送/接收邮件。
流程如下:
1.建立TCP连接并域服务器交互
图3
如图3所示与qq邮件服务器建立连接,域名"smtp.qq.com",SMTP默认端口号25。建立连接后服务器将返回状态码220;与服务器的交互:发送"HELO"命令,开始与服务器的交互,服务器将返回状态码250(请求动作正确完成)。
2.验证身份
发送"AUTH LOGIN"命令,开始验证身份,服务器将返回状态码334(服务器等待用户输入验证信息)。发送经过base64编码的用户名,服务器等待用户输入验证信息,将返回状态码334。发送经过base64编码的密码(不是登录密码,而是开启SMTP时的授权码),用户验证成功,服务器将返回状态码235如图4所示。
图4
3.邮件传送
msg = "\r\n I love computer networks!" endMsg = "\r\n.\r\n" # 选择一个邮件服务 mailServer = "smtp.qq.com" # 发送方地址和接收方地址,from 和 to fromAddress = "2485947593@qq.com" toAddress = "3050387300@qq.com" |
图5
连接建立并且验证成功后,就可开始传送邮件。邮件的传送从MAIL FROM命令开始,MAIL 命令后面有发件人的地址。MAIL FROM: 2485947593@qq.com。若SMTP服务器已准备好接收邮件,则回答250 OK。接着SMTP客户端发送一个或多个RCPT (收件人recipient的缩写)命令,格式为RCPT TO: <3050387300@qq.com>。每发送一个 RCPT命令,都应有相应的信息从SMTP服务器返回。正常情况下,SMTP服务器回复信息是354 Start mail input; end with . 。表示回车换行。此时SMTP客户端就可开始传送邮件内容,并用. (两个回车,中间一个点)表示邮件内容的结束。
4.连接释放
邮件发送完毕后,SMTP客户应发送QUIT命令。SMTP服务器返回的信息是221 (服务关闭),表示SMTP同意释放TCP连接。邮件传送的全部过程就此结束。同时,我们还可以登陆邮箱,查看邮件收发情况如图6、7所示。
图6
图7
4.源码
# SMTPClient.py
from socket import *
msg = "\r\n I love computer networks!"
endMsg = "\r\n.\r\n"
# 选择一个邮件服务
mailServer = "smtp.qq.com"
# 发送方地址和接收方地址,from 和 to
fromAddress = "2485947593@qq.com"
toAddress = "3050387300@qq.com"
# 发送方,验证信息,由于邮箱输入信息会使用base64编码,因此需要进行编码
username = "MjQ4NTk0NzU5M0BxcS5jb20=" # 输入自己的用户名对应的编码
password = "ZHRobnlvZmVyZGZsZWFpYg==" # 此处不是自己的密码,而是开启SMTP服务时对应的授权码dthnyoferdfleaib
# 创建客户端套接字并建立连接
serverPort = 25 # SMTP使用25号端口
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailServer, serverPort)) # connect只能接收一个参数
# 从客户套接字中接收信息
recv = clientSocket.recv(1024).decode()
print(recv)
if '220' != recv[:3]:
print('220 reply not received from server.')
# 发送 HELO 命令并且打印服务端回复
# 开始与服务器的交互,服务器将返回状态码250,说明请求动作正确完成
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand.encode()) # 随时注意对信息编码和解码
recv1 = clientSocket.recv(1024).decode()
print(recv1)
if '250' != recv1[:3]:
print('250 reply not received from server.')
# 发送"AUTH LOGIN"命令,验证身份.服务器将返回状态码334(服务器等待用户输入验证信息)
clientSocket.sendall('AUTH LOGIN\r\n'.encode())
recv2 = clientSocket.recv(1024).decode()
print(recv2)
if '334' != recv2[:3]:
print('334 reply not received from server.')
# 发送验证信息
clientSocket.sendall((username + '\r\n').encode())
recvName = clientSocket.recv(1024).decode()
print(recvName)
if '334' != recvName[:3]:
print('334 reply not received from server')
clientSocket.sendall((password + '\r\n').encode())
recvPass = clientSocket.recv(1024).decode()
print(recvPass)
# 如果用户验证成功,服务器将返回状态码235
if '235' != recvPass[:3]:
print('235 reply not received from server')
# TCP连接建立好之后,通过用户验证就可以开始发送邮件。邮件的传送从MAIL命令开始,MAIL命令后面附上发件人的地址。
# 发送 MAIL FROM 命令,并包含发件人邮箱地址
clientSocket.sendall(('MAIL FROM: <' + fromAddress + '>\r\n').encode())
recvFrom = clientSocket.recv(1024).decode()
print(recvFrom)
if '250' != recvFrom[:3]:
print('250 reply not received from server')
# 接着SMTP客户端发送一个或多个RCPT (收件人recipient的缩写)命令,格式为RCPT TO: <收件人地址>。
# 发送 RCPT TO 命令,并包含收件人邮箱地址,返回状态码 250
clientSocket.sendall(('RCPT TO: <' + toAddress + '>\r\n').encode())
recvTo = clientSocket.recv(1024).decode() # 注意UDP使用sendto,recvfrom
print(recvTo)
if '250' != recvTo[:3]:
print('250 reply not received from server')
# 发送 DATA 命令,表示即将发送邮件内容。服务器将返回状态码354(开始邮件输入,以"."结束)
clientSocket.send('DATA\r\n'.encode())
recvData = clientSocket.recv(1024).decode()
print(recvData)
if '354' != recvData[:3]:
print('354 reply not received from server')
# 编辑邮件信息,发送数据
subject = "I love computer networks!"
contentType = "text/plain"
message = 'from:' + fromAddress + '\r\n'
message += 'to:' + toAddress + '\r\n'
message += 'subject:' + subject + '\r\n'
message += 'Content-Type:' + contentType + '\t\n'
message += '\r\n' + msg
clientSocket.sendall(message.encode())
# 以"."结束。请求成功返回 250
clientSocket.sendall(endMsg.encode())
recvEnd = clientSocket.recv(1024).decode()
print(recvEnd)
if '250' != recvEnd[:3]:
print('250 reply not received from server')
# 发送"QUIT"命令,断开和邮件服务器的连接
clientSocket.sendall('QUIT\r\n'.encode())
clientSocket.close()
附:了解SMTP协议(简单邮件传输协议)常见命令和响应
命令:
HELO——发送端的主机信息
MAIL FROM——发件人
RCPT TO—— 收件人,可以有多个
DATA——邮件主体内容
QUIT——断开连接
VRFY—— 需要对收件人名字进行验证
EXPN——需要扩展的邮件发送清单
HELP——命令名
响应:
220——服务器就绪
221——服务关闭
421——服务器未就绪,关闭传输信道
250——要求的邮件操作完成
501——参数格式错误