SMTP协议(Python实现)

本文介绍了SMTP(简单邮件传输协议)的工作原理,包括其与POP3和IMAP的区别,以及SMTP命令和应答代码。通过一个Python代码示例展示了如何使用SMTP发送邮件,包括建立连接、身份验证、发送邮件内容和结束会话的过程。同时,概述了邮件报文的格式规范。
摘要由CSDN通过智能技术生成

SMTP介绍

过多基础概念我就不讲了, 直接翻书或者维基百科,
简单来说SMTP就是简单邮件传输协议, 既然是传输协议就是发邮件收邮件, 两者都有.
POP3IMAP区别开来
从这里简单可以看出区别
在这里插入图片描述

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

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. 除非需要使用回车与换行符,否则报文中不使用回车与换行符
    https://img-blog.csdn.net/20170403164927322?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU2NDQyMzQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

具体传输过程

先给代码和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 授权成功

发送邮件

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

连接终止

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

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值