socket编程实例——邮件客户端

2023-10-21 11:27:55 浏览数 (2)

这个编程作业的目的是创建一个向任何接收方发送电子邮件的简单邮件客户。你的客户必须与邮件服务器创建一个TCP连接,使用SMTP协议与邮件服务器进行交谈,经过该邮件服务器向某接收方发送一个电子邮件报文,最后关闭与该邮件服务器的TCP连接。

建立TCP连接

首先与邮件服务器建立TCP连接。这里用的是QQ邮箱的邮件服务器,使用其他厂商的邮件服务器也是一样的。

首先引入相关包,然后指定邮件服务器的地址和端口(邮件服务器端口默认为25),然后创建socket对象。

Python中socket对象的方法如下图所示

然后使用connect()方法与邮件服务器建立TCP连接

代码语言:javascript复制
from socket import *
import base64

endmsg = "rn.rn"

#smtp服务器和端口
serverName =  'smtp.qq.com'
serverPort = 25

# 创建socket对象
clientSocket = socket(AF_INET,SOCK_STREAM)
# 创建一个tcp连接
clientSocket.connect((serverName,serverPort))

确认连接并向邮件服务器发送消息

首先使用recv()方法从socket接口获取信息,如果状态码为220,则表示服务器准备就绪。

关于与邮件服务器对话的命令,参考下图的例子,HELO、MAIL FROM、RCPT TO、DATA以及QUIT。这些命令都是自解释的,具体作用在后面的代码会举例实现。每个报文以回车和换行结束,服务器会对每条命令做出应答,包含状态码和一些描述。你也可以使用下面的命令直接与一台邮件服务器对话:

代码语言:javascript复制
telnet smtp.qq.com 25

然后使用send()方法向邮件服务器发送编码过后HELO指令和消息,然后再从socket中读取消息。

代码语言:javascript复制
# 使用recv()从socket中读取数据,1024表示缓冲区大小,然后解码
recv = clientSocket.recv(1024).decode()
print(recv)

# 响应码为220,表示服务器准备就绪
if recv[:3] != '220':
    print('220 reply not received from server.')

# 向mail服务器发送信息
heloCommand = 'HELO QQrn'
# 使用send()在socket中写入信息
clientSocket.send(heloCommand.encode())

# 从socket中读取信息
recv1 = clientSocket.recv(1024).decode()
print("revc1="   recv1)

# 响应码为250,表示服务器完成了请求
if recv1[:3] != '250':
    print('250 reply not received from server.')

邮件的来与去

下面几次对话告诉邮件服务器邮件的来源与邮件的目的地。

代码语言:javascript复制
# 我的邮箱地址
my_address = "xxx@qq.com"
from_msg = 'MAIL FROM: <'   my_address   '>rn'
# 发送邮件来源
clientSocket.send(from_msg.encode())

# 从socket中读取信息
recv2 = clientSocket.recv(1024).decode()
print("revc2="   recv2)
# 响应码为250,表示服务器完成了请求
if recv2[:3] != '250':
    print('250 reply not received from server.')

# 目的地
destination = "xxx@qq.com"
des_msg = 'RCPT TO: <'   destination   '>rn'
# 发送邮件目的地
clientSocket.send(des_msg.encode())
# 从socket中读取信息
recv3 = clientSocket.recv(1024).decode()
print("revc3="   recv3)
# 响应码为250,表示服务器完成了请求
if recv3[:3] != '250':
    print('250 reply not received from server.')

认证

要使用QQ的邮件服务器发送邮件,还需要登录认证。

点击设置

然后在账户栏开启SMTP服务,并获取授权码

然后在下面填上自己的QQ邮箱与授权码。与邮件服务器对话完成身份认证,然后就可以使用SMTP服务了。

代码语言:javascript复制
# 登录认证
#以auth login方式登录,返回username的base64编码
login = 'AUTH LOGINrn'
clientSocket.send(login.encode())
recv2 = clientSocket.recv(1024)
print('login:',recv2.decode())
#输入用户名
username=base64.b64encode(b'xxx@qq.com').decode()   'rn'
clientSocket.send(username.encode())
recv_username = clientSocket.recv(1024)
print('username:', recv_username.decode())
#输入授权码,此处授权码需要使用qq邮箱开启
password=base64.b64encode(b'授权码').decode()   'rn'
clientSocket.send(password.encode())
recv_password = clientSocket.recv(1024)
print('password: ', recv_password.decode())

发送邮件

使用DATA指令,开始向邮件服务器发送邮件。

邮件需要包含各种首部信息,这些环境信息包含在一系列首部行中,由RFC 5322定义,与HTTP报文首部类似,有些是必须的,有些是可选的。每个首部必须包含一个From、To,可以包含Subject等。这些首部信息与前面的HELO等指令不同,那些命令是SMTP握手协议的一部分,而这些内容是邮件报文自身的部分。

然后将报文塞进socket即可。

最后使用QUIT指令关闭与邮件服务的连接。

代码语言:javascript复制
# 准备发送邮件
DATA = 'DATArn'
clientSocket.send(DATA.encode())
recv4 = clientSocket.recv(1024).decode()
print("revc4="   recv4)
# 响应码为354
if recv4[:3] != '354':
    print('345 reply not received from server.')

# 发送邮件
headers = [
    'From: '   from_msg,
    'To: '   des_msg,
    'Subject: 主题',
]

body = [
    "内容2",
    "内容1",
]
message = 'rnrn'.join(('rn'.join(headers), 'rn'.join(body)))

clientSocket.send(message.encode())
clientSocket.send(endmsg.encode())
recv5 = clientSocket.recv(1024).decode()
print("revc5="   recv5)
if recv5[:3] != '250':
    print('250 reply not received from server.')

# 结束
quit_msg = "QUITrn"
clientSocket.send(quit_msg.encode())
recv6 = clientSocket.recv(1024).decode()
print("revc6="   recv6)

# 响应码为221,bye!
if recv6[:3] != '221':
    print('221 reply not received from server.')

完整代码

代码语言:javascript复制
from socket import *
import base64

endmsg = "rn.rn"

#smtp服务器和端口
serverName =  'smtp.qq.com'
serverPort = 25

# 创建socket对象
clientSocket = socket(AF_INET,SOCK_STREAM)
# 创建一个tcp连接
clientSocket.connect((serverName,serverPort))

# 使用recv()从socket中读取数据,1024表示缓冲区大小,然后解码
recv = clientSocket.recv(1024).decode()
print(recv)

# 响应码为220,表示服务器准备就绪
if recv[:3] != '220':
    print('220 reply not received from server.')

# 向mail服务器发送信息
heloCommand = 'HELO QQrn'
# 使用send()在socket中写入信息
clientSocket.send(heloCommand.encode())

# 从socket中读取信息
recv1 = clientSocket.recv(1024).decode()
print("revc1="   recv1)

# 响应码为250,表示服务器完成了请求
if recv1[:3] != '250':
    print('250 reply not received from server.')


# 登录认证
#以auth login方式登录,返回username的base64编码
login = 'AUTH LOGINrn'
clientSocket.send(login.encode())
recv2 = clientSocket.recv(1024)
print('login:',recv2.decode())
#输入用户名
username=base64.b64encode(b'xxx@qq.com').decode()   'rn'
clientSocket.send(username.encode())
recv_username = clientSocket.recv(1024)
print('username:', recv_username.decode())

#输入授权码,此处授权码需要使用qq邮箱开启
password=base64.b64encode(b'授权码').decode()   'rn'
clientSocket.send(password.encode())
recv_password = clientSocket.recv(1024)
print('password: ', recv_password.decode())

# 我的邮箱地址
my_address = "xxx@qq.com"
from_msg = 'MAIL FROM: <'   my_address   '>rn'
# 发送邮件来源
clientSocket.send(from_msg.encode())

# 从socket中读取信息
recv2 = clientSocket.recv(1024).decode()
print("revc2="   recv2)
# 响应码为250,表示服务器完成了请求
if recv2[:3] != '250':
    print('250 reply not received from server.')

# 目的地
destination = "xxx@qq.com"
des_msg = 'RCPT TO: <'   destination   '>rn'
# 发送邮件目的地
clientSocket.send(des_msg.encode())
# 从socket中读取信息
recv3 = clientSocket.recv(1024).decode()
print("revc3="   recv3)
# 响应码为250,表示服务器完成了请求
if recv3[:3] != '250':
    print('250 reply not received from server.')


# 准备发送邮件
DATA = 'DATArn'
clientSocket.send(DATA.encode())
recv4 = clientSocket.recv(1024).decode()
print("revc4="   recv4)
# 响应码为354
if recv4[:3] != '354':
    print('345 reply not received from server.')

# 发送邮件
headers = [
    'From: '   from_msg,
    'To: '   des_msg,
    'Subject: 邮件主题',
]

body = [
    "内容1",
    "内容2",
]
message = 'rnrn'.join(('rn'.join(headers), 'rn'.join(body)))

clientSocket.send(message.encode())
clientSocket.send(endmsg.encode())
recv5 = clientSocket.recv(1024).decode()
print("revc5="   recv5)
if recv5[:3] != '250':
    print('250 reply not received from server.')

# 结束
quit_msg = "QUITrn"
clientSocket.send(quit_msg.encode())
recv6 = clientSocket.recv(1024).decode()
print("revc6="   recv6)

# 响应码为221,bye!
if recv6[:3] != '221':
    print('221 reply not received from server.')

0 人点赞