SMTP协议(Python实现) SMTP介绍 SMTP命令和应答代码 邮件的报文格式 具体传输过程

过多基础概念我就不讲了, 直接翻书或者维基百科,
简单来说SMTP就是简单邮件传输协议, 既然是传输协议就是发邮件收邮件, 两者都有.
POP3IMAP区别开来
从这里简单可以看出区别
SMTP协议(Python实现)
SMTP介绍
SMTP命令和应答代码
邮件的报文格式
具体传输过程

  • 工作过程
    其实就是收发邮件的双方都找了中间代理. SMTP是建立在TCP连接且端口为25的应用层协议. 主要是非实时的, 因为有的客户只会定期(15min)去询问代理邮箱有没有新的邮件
    SMTP协议(Python实现)
SMTP介绍
SMTP命令和应答代码
邮件的报文格式
具体传输过程
    这里就不进行文字赘述了, 等下实操代码用图片来分析流程

SMTP命令和应答代码

应答代码, 服务器的返回消息

代码 说明 解决方案
220 一般建立连接后就返回这个消息
250 完成一个请求操作/命令后返回的消息
354 邮件具体内容的传输说明
503 命令顺序错误了/需要授权 一般先检查几个关键命令的顺序,再检查授权/登录错误
502 命令没有实现
命令 参数
HELO 主机名/对方名字
MAIL FROM 自己邮箱
RCPT TO 对方邮箱
DATA 开始传输数据, 邮件的主体
AUTH LOGIN 授权 一般就直接用LOGIN登录

邮件的报文格式

RFC文档的对报文格式的定义

  1. 所有报文都是由ASCII码组成
  2. 报文由报文行组成,各行之间用回车(CR)、换行(LF)符分隔
  3. 报文的长度不能超过998个字符
  4. 报文行的长度≤78个字符之内(不包括回车换行符)
  5. 报文中可包括多个首部字段和首部内容
  6. 报文可包括一个主体,主体必须用一个空行与其首部分隔
  7. 除非需要使用回车与换行符,否则报文中不使用回车与换行符
    SMTP协议(Python实现)
SMTP介绍
SMTP命令和应答代码
邮件的报文格式
具体传输过程

具体传输过程

先给代码和wireshark

from socket import *
import base64

# Mail content
subject = "I love computer networks!"
contenttype = "text/plain"
msg = "I love computer networks!"
endmsg = "
.
"

# 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
'
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
'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
	print('334 reply not received from server')

clientSocket.sendall((username + '
').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
	print('334 reply not received from server')

clientSocket.sendall((password + '
').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+'>
').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+'>
').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
').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 + '
'
message += 'to:' + toaddress + '
'
message += 'subject:' + subject + '
'
message += 'Content-Type:' + contenttype + '
'
message += '
' + 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
'.encode())
# Fill in end

clientSocket.close()

SMTP协议(Python实现)
SMTP介绍
SMTP命令和应答代码
邮件的报文格式
具体传输过程

先建立连接

先用socket建立TCP连接

使用邮件服务器和端口号进行握手, 建立TCP连接, 服务器返回220
客户端接收到220之后 发送HELO命令, 发起会话
服务器返回250, 很OK

连接建立后一般要登录了,

因为不是自己建立的邮件服务器, 是企业/别人的, 需要授权登入

客户端发送命令AUTH LOGIN 我要登录了
服务器回答 334 dXNlcm5hbWU6 意思是 输入 username: 后者是base64加密编码
客户发送加密后的账户名和密码
服务器回答 235 授权成功

发送邮件

  1. 客户用“MAIL FROM”向服务器报告发信人的邮箱与域名
  2. 服务器向客户回应应答码“250”,代表请求命令完成
  3. 客户用“RCPT TO”命令向服务器报告收信人的邮箱与域名
  4. 服务器向客户回应应答码“250”,代表请求命令完成
  5. 客户用“DTAT”命令对报文的传送进行初始化
  6. 服务器回应“354”,表示可以进行邮件输入了
  7. 客户用连续的行向服务器传送报文的内容,每行以两字符的行结束标识(CR与LF)终止。报文以只有一个“.”的行结束
  8. 服务器向客户回应应答码“250”,代表请求命令完成

连接终止

  • 客户端发送“QUIT”命令
  • 服务器收到命令后,回应应答码“221”,并结束会话

SMTP协议(Python实现)
SMTP介绍
SMTP命令和应答代码
邮件的报文格式
具体传输过程