设计原理
建立网络连接:客户程序首先需要建立与邮件服务器的网络连接。它可以使用标准的TCP/IP协议通过邮件服务器的标准SMTP端口(通常是25号端口)来建立连接。
发送邮件头部信息:客户程序需要构建合法的邮件头部信息。这包括发件人、收件人、主题、日期等。这些信息通常以文本形式编码,并遵循RFC 5322规范。
验证发件人身份:某些邮件服务器要求客户程序在发送邮件之前验证发件人的身份。这可以通过SMTP协议的认证机制实现,例如使用用户名和密码进行登录认证。
发送正文和附件:客户程序需要将邮件的正文内容和附件(如果有)编码,并通过SMTP协议发送给邮件服务器。正文内容通常使用MIME(Multipurpose Internet Mail Extensions)格式进行编码,以支持不同类型的内容,如纯文本、HTML、图像等。
处理错误和回应:客户程序在发送邮件后需要等待邮件服务器的响应。服务器可能返回成功的响应码(如250),表示邮件已成功发送,或返回错误的响应码(如550),表示发送失败。客户程序需要相应地处理这些响应,以便根据需要采取适当的行动。
断开连接:发送完邮件后,客户程序可以选择断开与邮件服务器的连接,释放资源,并结束与服务器的通信。
需要注意的是,SMTP是一个简单的文本协议,它定义了客户程序与邮件服务器之间的通信规则。因此,设计SMTP邮件发送客户程序时,需要遵循SMTP协议的规范,确保与邮件服务器之间的交互符合协议要求(如RFC5322、RFC2047 和 RFC822 标准协议等)。
程序流程图
开始
┌─────────────────────────────────────────────┐
│ 连接邮件服务器 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 验证发件人身份(可选) │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 构建邮件头部信息 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 发送正文和附件 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 等待邮件服务器响应 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 处理邮件服务器回应 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 断开与邮件服务器的连接 │
└─────────────────────────────────────────────┘
结束
设计步骤
导入所需的模块:代码开头导入了smtplib、sys以及来自email.mime.text、email.header和email.utils模块的特定类和函数。这些模块和类用于发送邮件和构建邮件内容。导入from tkinter import *,from tkinter import messagebox模块进行可视化设计。
定义send_email函数:函数接受发件人信息、收件人信息、主题、内容等参数,用于发送邮件。在函数内部,首先根据指定的smtp_server选择不同的SMTP服务器进行设置。
设置邮件内容:使用MIMEText类创建邮件正文内容,并设置发件人、收件人、主题等邮件头部信息。
根据smtp_server选择SMTP服务器:根据用户选择的smtp_server的值,设置相应的SMTP服务器地址和端口。对于某些SMTP服务器,还需要调用starttls()方法启用TLS加密。
建立连接并登录SMTP服务器:使用SMTP或SMTP_SSL类创建SMTP对象,并调用connect()方法连接到SMTP服务器。然后调用login()方法使用发件人的邮箱地址和密码进行登录。
发送邮件:调用sendmail()方法将邮件发送给收件人。其中,as_string()方法将msg对象转换为字符串形式。
处理异常:使用try-except块捕获SMTPException异常,打印发送失败的错误信息。
关闭连接:在finally块中,使用quit()方法关闭与SMTP服务器的连接。
主程序:获取用户输入的SMTP服务器选择以及发件人、收件人、主题、内容等信息,并调用send_email函数发送邮件。
使用方法说明
首先,因为我们使用的是基于SMTP协议的邮箱发送,所以在开始实验前要确保所使用的运营商要支持SMTP传输,这里我以QQ邮箱为例,它是支持SMTP传输的,但是需要自己打开。
这里QQ邮箱官方关于SMTP服务有明确的说明。设置完后我们会得到一个授权码,这个授权码就将作为为我们提供SMTP服务的实际“密码”。
这里我已经开启了SMTP服务,接下来实验我将以QQ邮箱为服务商进行演示。
运行SMTP_Email.py文件。运行界面如下:
(界面设计的比较丑,因为就只是运用GUI最基础的部分,也没有封装函数,所有图形化设置都在main函数里实现)
这里我们需要先在右上角选择邮箱运营商,这里我设置了4个可以使用SMTP的邮箱。如果不选邮箱就发送系统会提醒我们选择运营商:
这里我们选择QQ邮箱进行演示。
显示我们已经切换成功了,接下来就是按照提示输入信息,且信息都不能为空,否则系统会进行提醒:
我们按照提醒输入信息后发送:
这里系统提示我们已经发送成功
我们更改一下错误消息看它还能不能成功发送,我们先将发送方邮箱密码更改(为了保密密码输入时将变为‘*’):
系统提示我们连接失败,说明错误的密码(授权码)没有让我们登上QQ邮箱。
我接下来更改接收方的信息:
结果及分析
接下来我们进入QQ邮箱官网验证我们之前发出去的两封信是不是都发送到了。
通过查询发送记录,我们发现第一份信已经准确送达了:
由于接收方的邮箱地址不存在,邮件最后被退了回来,这也说明了SMTP传输服务只负责发出去,而不会验证接收方是否存在。
#SMTP_Email.py文件代码如下:
from tkinter import *
from tkinter import messagebox
import smtplib
import sys
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr
#邮箱服务类型选择
smtp_server = '0'
def QQ():
global smtp_server
smtp_server='1'
messagebox.showinfo('提醒','你已成功切换为QQ邮箱!')
def SOUHU():
global smtp_server
smtp_server='2'
messagebox.showinfo('提醒','你已成功切换为搜狐邮箱!')
def GMAIL():
global smtp_server
smtp_server='3'
messagebox.showinfo('提醒','你已成功切换为Gmail!')
def YAHOO():
global smtp_server
smtp_server='4'
messagebox.showinfo('提醒','你已成功切换为雅虎邮箱!')
def send_email(sender, password, receiver, subject, message, smtp_server,user_name):
# 邮件内容设置
msg = MIMEText(message, 'plain', 'utf-8')
msg['From'] = formataddr((str(Header(user_name, 'utf-8')), sender))
msg['To'] = Header(receiver, 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
smtp_obj = None # 初始化smtp_obj变量
try:
if smtp_server == '1':
# QQ 邮箱的SMTP服务器设置
SMTP_IP='smtp.qq.com'
PORT=465
smtp_obj = smtplib.SMTP_SSL('smtp.qq.com', 465)
elif smtp_server == '2':
# Sohu 邮箱的SMTP服务器设置
SMTP_IP = 'smtp.sohu.com'
PORT = 25
smtp_obj = smtplib.SMTP('smtp.sohu.com', 25)
elif smtp_server == '3':
# Gmail 的SMTP服务器设置
SMTP_IP = 'smtp.gmail.com'
PORT = 587
smtp_obj = smtplib.SMTP('smtp.gmail.com', 587)
smtp_obj.starttls()
elif smtp_server == '4':
# yahoo 的SMTP服务器设置
SMTP_IP = 'smtp.yahoo.cn'
PORT = 465
smtp_obj = smtplib.SMTP('smtp.yahoo.cn', 465)
smtp_obj.starttls()
else:
#print("不支持的SMTP服务器")
root1 = Tk()
root1.geometry("400x20+500+200")
root1.title('系统消息')
label_3 = Label(root1, text='不支持的SMTP服务器!', font=("宋体", 10), fg='red')
label_3.grid(row=1, column=1)
root1.mainloop()
return
# 建立连接
smtp_obj.connect(SMTP_IP, PORT)
# 登录SMTP服务器
smtp_obj.login(sender, password)
# 发送邮件
smtp_obj.sendmail(sender, receiver, msg.as_string())
#print("邮件发送成功")
root1 = Tk()
root1.geometry("400x20+500+200")
root1.title('系统消息')
label_3 = Label(root1, text='邮件发送成功!', font=("宋体", 10), fg='red')
label_3.grid(row=1, column=1)
root1.mainloop()
except smtplib.SMTPException as e:
#print("邮件发送失败:" + str(e))
root1 = Tk()
root1.geometry("400x20+500+200")
root1.title('系统消息')
label_3 = Label(root1, text="邮件发送失败:!"+str(e), font=("宋体", 10), fg='red')
label_3.grid(row=1, column=1)
root1.mainloop()
finally:
if smtp_obj:
# 关闭连接
smtp_obj.quit()
def func():
global smtp_server
sender_email = str(entry_1.get())
sender_password = str(entry_2.get())
user_name = str(entry_3.get())
receiver_email = str(entry_4.get())
email_subject = str(entry_5.get())
email_message = entry_6.get("1.0", "end")
if smtp_server == '0':
messagebox.showwarning('WARING!', '你还未选择邮箱运营商!')
elif sender_email == "":
messagebox.showwarning('WARING!', '发送邮箱地址不能为空!')
elif sender_password == "":
messagebox.showwarning('WARING!', '密码(授权码)不能为空!')
elif user_name == "":
messagebox.showwarning('WARING!', '用户名不能为空!')
elif receiver_email == "":
messagebox.showwarning('WARING!', '接收邮箱地址不能为空!')
elif email_subject == "":
messagebox.showwarning('WARING!', '邮件主题不能为空!')
elif email_message == "":
messagebox.showwarning('WARING!', '邮件内容不能为空!')
else:
send_email(sender_email, sender_password, receiver_email, email_subject, email_message, smtp_server, user_name)
if __name__ =='__main__':
root=Tk()
root.geometry("650x500+350+180")
root.title('Internet 邮件发送客户程序(使用前请先确保您的邮箱开通了SMTP功能)')
#标签
label_1=Label(root,text='请输入发送方邮箱地址:',font=("楷书",10),fg="blue")
label_1.grid(row=0,column=0)
#输入框
entry_1=Entry(root,font=("宋体",10),fg="red")
entry_1.grid(row=1,column=0)
label_2=Label(root,text='请输入发送方邮箱密码:',font=("楷书",10),fg="blue")
label_2.grid(row=2,column=0)
entry_2=Entry(root,font=("宋体",10),fg="red",show='*')
entry_2.grid(row=3,column=0)
label_3=Label(root,text='请输入发送方用户名:',font=("楷书",10),fg="blue")
label_3.grid(row=4,column=0)
entry_3=Entry(root,font=("宋体",10),fg="red")
entry_3.grid(row=5,column=0)
label_4=Label(root,text='请输入接收方邮箱地址:',font=("楷书",10),fg="blue")
label_4.grid(row=6,column=0)
entry_4=Entry(root,font=("宋体",10),fg="red")
entry_4.grid(row=7,column=0)
label_5=Label(root,text='请输入发送邮件的主题:',font=("楷书",10),fg="blue")
label_5.grid(row=8,column=0)
entry_5=Entry(root,font=("宋体",10),fg="red")
entry_5.grid(row=9,column=0)
label_6=Label(root,text='请输入发送邮件的内容:',font=("楷书",10),fg="blue")
label_6.grid(row=10,column=0)
entry_6=Text(root,font=("宋体",10),fg="red",width=80,height=40)
entry_6.grid(row=11,column=0)
#按钮
button=Button(root,text='QQ邮箱',font=("楷书",10),fg="black",command=QQ)
button.grid(row=0,column=2)
button=Button(root,text='搜狐邮箱',font=("楷书",10),fg="black",command=SOUHU)
button.grid(row=1,column=2)
button=Button(root,text='Gmail',font=("楷书",10),fg="black",command=GMAIL)
button.grid(row=2,column=2)
button=Button(root,text='雅虎邮箱',font=("楷书",10),fg="black",command=YAHOO)
button.grid(row=3,column=2)
button=Button(root,text='发送',font=("楷书",25),fg="green",command=func)
button.grid(row=10,column=2)
root.mainloop()