SMTP介绍
过多基础概念我就不讲了, 直接翻书或者维基百科,
简单来说SMTP就是简单邮件传输协议, 既然是传输协议就是发邮件和收邮件, 两者都有.
和POP3和IMAP区别开来
从这里简单可以看出区别
- 工作过程
其实就是收发邮件的双方都找了中间代理. SMTP是建立在TCP连接且端口为25的应用层协议. 主要是非实时的, 因为有的客户只会定期(15min)去询问代理邮箱有没有新的邮件
这里就不进行文字赘述了, 等下实操代码用图片来分析流程
SMTP命令和应答代码
应答代码, 服务器的返回消息
代码 | 说明 | 解决方案 |
---|---|---|
220 | 一般建立连接后就返回这个消息 | |
250 | 完成一个请求操作/命令后返回的消息 | |
354 | 邮件具体内容的传输说明 | |
503 | 命令顺序错误了/需要授权 | 一般先检查几个关键命令的顺序,再检查授权/登录错误 |
502 | 命令没有实现 |
命令 | 参数 |
---|---|
HELO | 主机名/对方名字 |
MAIL FROM | 自己邮箱 |
RCPT TO | 对方邮箱 |
DATA | 开始传输数据, 邮件的主体 |
AUTH LOGIN | 授权 一般就直接用LOGIN登录 |
邮件的报文格式
RFC文档的对报文格式的定义
- 所有报文都是由ASCII码组成
- 报文由报文行组成,各行之间用回车(CR)、换行(LF)符分隔
- 报文的长度不能超过998个字符
- 报文行的长度≤78个字符之内(不包括回车换行符)
- 报文中可包括多个首部字段和首部内容
- 报文可包括一个主体,主体必须用一个空行与其首部分隔
- 除非需要使用回车与换行符,否则报文中不使用回车与换行符
具体传输过程
先给代码和wireshark
from socket import *
import base64
# Mail content
subject = "I love computer networks!"
contenttype = "text/plain"
msg = "I love computer networks!"
endmsg = "\r\n.\r\n"
# Choose a mail server (e.g. Google mail server) and call it mailserver
# 不清楚的直接搜大学/企业邮箱服务器即可, 大部分都是25端口的发送端口
mailserver = "***.mail"
# Sender and reciever
fromaddress = "****@mail"
toaddress = "****@qq.com"
# Auth information (Encode with base64)
username = base64.b64encode(fromaddress.encode()).decode()
password = base64.b64encode("******".encode()).decode()
# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailserver, 25))
recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '220':
print('220 reply not received from server.')
# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024).decode()
print(recv1)
if recv1[:3] != '250':
print('250 reply not received from server.')
# Auth必须在hello之后再进行授权
clientSocket.sendall('AUTH LOGIN\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
print('334 reply not received from server')
clientSocket.sendall((username + '\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
print('334 reply not received from server')
clientSocket.sendall((password + '\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '235'):
print('235 reply not received from server')
# Send MAIL FROM command and print server response.
# Fill in start
#sendall将发送全部数据直到错误或者全部发送完, send发送少于所需要的字节数
clientSocket.sendall(('MAIL FROM: <'+fromaddress+'>\r\n').encode())
recv2 = clientSocket.recv(1024).decode()
print(recv2)
if (recv2[:3] != '250'):
print('250 reply not received from server.')
# Fill in end
# Send RCPT TO command and print server response.
# Fill in start 这里的命令错了 不是MAIL是RCPT
clientSocket.sendall(('RCPT TO: <'+toaddress+'>\r\n').encode())
recv3 = clientSocket.recv(1024).decode()
print(recv3)
if (recv3[:3] != '250'):
print('250 reply not received from server.')
# Fill in end
# Send DATA command and print server response.
# Fill in start
clientSocket.send(('DATA\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '354'):
print('354 reply not received from server')
# Fill in end
# Send message data.
# Fill in start
message = 'from:' + fromaddress + '\r\n'
message += 'to:' + toaddress + '\r\n'
message += 'subject:' + subject + '\r\n'
message += 'Content-Type:' + contenttype + '\r\n'
message += '\r\n' + msg
clientSocket.sendall(message.encode())
# Fill in end
# Message ends with a single period.
# Fill in start
clientSocket.sendall(endmsg.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
print('250 reply not received from server')
# Fill in end
# Send QUIT command and get server response.
# Fill in start
clientSocket.sendall('QUIT\r\n'.encode())
# Fill in end
clientSocket.close()
先建立连接
先用socket建立TCP连接
使用邮件服务器和端口号进行握手, 建立TCP连接, 服务器返回220
客户端接收到220之后 发送HELO命令, 发起会话
服务器返回250, 很OK
连接建立后一般要登录了,
因为不是自己建立的邮件服务器, 是企业/别人的, 需要授权登入
客户端发送命令AUTH LOGIN 我要登录了
服务器回答 334 dXNlcm5hbWU6 意思是 输入 username: 后者是base64加密编码
客户发送加密后的账户名和密码
服务器回答 235 授权成功
发送邮件
- 客户用“MAIL FROM”向服务器报告发信人的邮箱与域名
- 服务器向客户回应应答码“250”,代表请求命令完成
- 客户用“RCPT TO”命令向服务器报告收信人的邮箱与域名
- 服务器向客户回应应答码“250”,代表请求命令完成
- 客户用“DTAT”命令对报文的传送进行初始化
- 服务器回应“354”,表示可以进行邮件输入了
- 客户用连续的行向服务器传送报文的内容,每行以两字符的行结束标识(CR与LF)终止。报文以只有一个“.”的行结束
- 服务器向客户回应应答码“250”,代表请求命令完成
连接终止
- 客户端发送“QUIT”命令
- 服务器收到命令后,回应应答码“221”,并结束会话