这部分回顾Socket Programming Assignment 3: SMTP。

参考资料:

简介

此次作业的目的是熟悉SMTP协议,使用socket开发一个邮件客户端,主要任务如下:

  • 一个基本的邮件客户端
  • 带SSL命令的邮件客户端
  • 实现能够收发文本和图像的客户端。

文件路径结构如下:

Socket3_SMTP/
|-- mail_client.py
|-- mail_client_ssl.py
|-- mail_client_text_pic.py
`-- picture.jpg

文件解释如下:

  • mail_client.py:基本的邮件客户端;
  • mail_client_ssl.py:带SSL命令的邮件客户端;
  • mail_client_text_pic.py:可以传送文本以及图片的邮件客户端;
  • picture.jpg:测试图片;

注意点:

  • 邮件服务器不要使用163邮件服务器,建议选用qq邮件服务器,注意需要开启POP3/SMTP服务,后续输入的账号密码为开启服务后的授权码。
  • 账号密码需要编码为base64,这部分可以参考如下资料:

发送邮件的步骤如下:

  • 和邮件服务器建立TCP连接;
  • 利用HELO命令像服务器表明身份;
  • 利用auth login授权登录(输入账号密码);
  • 利用MAIL FROM以及RCPT TO命令表明邮件的发送和接收方;
  • 利用DATA命令发送信息;

基本的邮件客户端

只要按照之前介绍的步骤实现即可,mail_client.py代码如下:

# coding=utf-8

from socket import *
import base64
from datetime import datetime
#msg = f"\r\n I love computer networks! {datetime.today().strftime('%Y-%m-%d-%H:%M:%S')}"
msg = "\r\n I love computer networks!"
endmsg = "\r\n.\r\n"
# Choose a mail server (e.g. Google mail server) and call it mailserver 
mailserver = "smtp.qq.com"
mailport = 587

# Create socket called clientSocket and establish a TCP connection with mailserver
#Fill in start
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailserver, mailport))

#Fill in end
# 220 <domain> Service ready
print("Start connect to smtp server!")
while True:
	recv = clientSocket.recv(1024).decode()
	print(recv)
	if recv[:3] != '220':
		print('220 reply not received from server.')
	else:
		print("Connect successfully!")
		break

# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
# 250 Requested mail action okay, completed
while True:
	clientSocket.send(heloCommand.encode())
	recv1 = clientSocket.recv(1024).decode()
	print(recv1)
	if recv1[:3] != '250':
		print('250 reply not received from server.')
	else:
		break

# 登陆
print("Start log in!")
authCommand = "auth login\r\n"
clientSocket.send(authCommand.encode())
recv2 = clientSocket.recv(1024)
print(recv2.decode())

# 邮箱
email = input("Input your email:")
emailCommand = base64.b64encode(email.encode()) + b'\r\n'
clientSocket.send(emailCommand)
recv3 = clientSocket.recv(1024)
print(recv3.decode())

# 密码
passWord = input("Input your password:")
passWordCommand = base64.b64encode(passWord.encode()) + b'\r\n'
clientSocket.send(passWordCommand)
recv4 = clientSocket.recv(1024)
print(recv4.decode())

# Send MAIL FROM command and print server response.
# Fill in start
mailCommand = f"MAIL FROM: <{email}>\r\n"
print(mailCommand)
clientSocket.send(mailCommand.encode())
recv5 = clientSocket.recv(1024)
print(recv5.decode())
# Fill in end

# Send RCPT TO command and print server response.
# Fill in start
destMail = input("Input your destMail:")
rcptCommand = f"RCPT TO: <{destMail}>\r\n"
print(rcptCommand)
clientSocket.send(rcptCommand.encode())
recv6 = clientSocket.recv(1024)
print(recv6.decode())
# Fill in end

# Send DATA command and print server response.
# Fill in start
print("Start send data!")
dataCommand = "DATA\r\n"
clientSocket.send(dataCommand.encode())
recv7 = clientSocket.recv(1024)
print(recv7.decode())
# Fill in end

# Send message data.
# Fill in start
clientSocket.send(msg.encode())
# Fill in end 

# Message ends with a single period.
# Fill in start
clientSocket.send(endmsg.encode())
# Fill in end

# Send QUIT command and get server response.
# Fill in start
quitCommand = "QUIT\r\n"
clientSocket.send(quitCommand.encode())
recv8 = clientSocket.recv(1024)
print(recv8.decode())
# Fill in end

启动客户端,输入账号密码以及接收方即可在接收方邮箱收到邮件:

带SSL命令的邮件客户端

更换端口,然后调用ssl.wrap_socket函数即可,mail_client_ssl.py代码如下:

# coding=utf-8

from socket import *
import base64
import ssl

#msg = f"\r\n I love computer networks! {datetime.today().strftime('%Y-%m-%d-%H:%M:%S')}"
msg = "\r\n I love computer networks!"
endmsg = "\r\n.\r\n"
# Choose a mail server (e.g. Google mail server) and call it mailserver 
mailserver = "smtp.qq.com"
mailport = 465

# Create socket called clientSocket and establish a TCP connection with mailserver
#Fill in start
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocketSSL = ssl.wrap_socket(clientSocket)
clientSocketSSL.connect((mailserver, mailport))

#Fill in end
# 220 <domain> Service ready
print("Start connect to smtp server!")
while True:
	recv = clientSocketSSL.recv(1024).decode()
	print(recv)
	if recv[:3] != '220':
		print('220 reply not received from server.')
	else:
		print("Connect successfully!")
		break

# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
# 250 Requested mail action okay, completed
while True:
	clientSocketSSL.send(heloCommand.encode())
	recv1 = clientSocketSSL.recv(1024).decode()
	print(recv1)
	if recv1[:3] != '250':
		print('250 reply not received from server.')
	else:
		break

# 登陆
print("Start log in!")
authCommand = "auth login\r\n"
clientSocketSSL.send(authCommand.encode())
recv2 = clientSocketSSL.recv(1024)
print(recv2.decode())

# 邮箱
email = input("Input your email:")
emailCommand = base64.b64encode(email.encode()) + b'\r\n'
clientSocketSSL.send(emailCommand)
recv3 = clientSocketSSL.recv(1024)
print(recv3.decode())

# 密码
passWord = input("Input your password:")
passWordCommand = base64.b64encode(passWord.encode()) + b'\r\n'
clientSocketSSL.send(passWordCommand)
recv4 = clientSocketSSL.recv(1024)
print(recv4.decode())

# Send MAIL FROM command and print server response.
# Fill in start
mailCommand = f"MAIL FROM: <{email}>\r\n"
print(mailCommand)
clientSocketSSL.send(mailCommand.encode())
recv5 = clientSocketSSL.recv(1024)
print(recv5.decode())
# Fill in end

# Send RCPT TO command and print server response.
# Fill in start
destMail = input("Input your destMail:")
rcptCommand = f"RCPT TO: <{destMail}>\r\n"
print(rcptCommand)
clientSocketSSL.send(rcptCommand.encode())
recv6 = clientSocketSSL.recv(1024)
print(recv6.decode())
# Fill in end

# Send DATA command and print server response.
# Fill in start
print("Start send data!")
dataCommand = "DATA\r\n"
clientSocketSSL.send(dataCommand.encode())
recv7 = clientSocketSSL.recv(1024)
print(recv7.decode())
# Fill in end

# Send message data.
# Fill in start
clientSocketSSL.send(msg.encode())
# Fill in end 

# Message ends with a single period.
# Fill in start
clientSocketSSL.send(endmsg.encode())
# Fill in end

# Send QUIT command and get server response.
# Fill in start
quitCommand = "QUIT\r\n"
clientSocketSSL.send(quitCommand.encode())
recv8 = clientSocketSSL.recv(1024)
print(recv8.decode())
# Fill in end

启动客户端,输入账号密码以及接收方即可在接收方邮箱收到邮件:

能够收发文本和图像的客户端

实现这点的重点是增加boundary,注意实际使用boundary时要增加前缀—,介绍资料如下:

其余部分和之前类似,完整的mail_client_text_pic.py内容如下:

# coding=utf-8

from socket import *
import base64

boundary = 'b--------------------------------'
with open("picture.jpg", "rb") as f:
    picdata = base64.b64encode(f.read())

# mail head
msg = f'Content-Type:multipart/related; boundary="{boundary}"\r\n\r\n'.encode()
msg += f'--{boundary}\r\n'.encode()

# text head
msg += 'Content-Type: text/html; charset=UTF-8\r\n'.encode()
msg += 'Content-Transfer-Encoding: base64\r\n\r\n'.encode()
# text, 不要包含\r\n
msg += base64.b64encode("I love computer networks!".encode())
msg += "\r\n".encode()
msg += f'--{boundary}\r\n'.encode()

# image head
msg += 'Content-Type: image/jpeg; name="picture.jpg"\r\n'.encode()
msg += 'Content-Transfer-Encoding: base64\r\n\r\n'.encode()
# image
msg += picdata + "\r\n".encode()
msg += f'--{boundary}\r\n'.encode()

endmsg = "\r\n.\r\n"
# Choose a mail server (e.g. Google mail server) and call it mailserver 
mailserver = "smtp.qq.com"
mailport = 587

# Create socket called clientSocket and establish a TCP connection with mailserver
#Fill in start
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailserver, mailport))

#Fill in end
# 220 <domain> Service ready
print("Start connect to smtp server!")
while True:
	recv = clientSocket.recv(1024).decode()
	print(recv)
	if recv[:3] != '220':
		print('220 reply not received from server.')
	else:
		print("Connect successfully!")
		break

# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
# 250 Requested mail action okay, completed
while True:
	clientSocket.send(heloCommand.encode())
	recv1 = clientSocket.recv(1024).decode()
	print(recv1)
	if recv1[:3] != '250':
		print('250 reply not received from server.')
	else:
		break

# 登陆
print("Start log in!")
authCommand = "auth login\r\n"
clientSocket.send(authCommand.encode())
recv2 = clientSocket.recv(1024)
print(recv2.decode())

# 邮箱
email = input("Input your email:")
emailCommand = base64.b64encode(email.encode()) + b'\r\n'
clientSocket.send(emailCommand)
recv3 = clientSocket.recv(1024)
print(recv3.decode())

# 密码
passWord = input("Input your password:")
passWordCommand = base64.b64encode(passWord.encode()) + b'\r\n'
clientSocket.send(passWordCommand)
recv4 = clientSocket.recv(1024)
print(recv4.decode())

# Send MAIL FROM command and print server response.
# Fill in start
mailCommand = f"MAIL FROM: <{email}>\r\n"
print(mailCommand)
clientSocket.send(mailCommand.encode())
recv5 = clientSocket.recv(1024)
print(recv5.decode())
# Fill in end

# Send RCPT TO command and print server response.
# Fill in start
destMail = input("Input your destMail:")
rcptCommand = f"RCPT TO: <{destMail}>\r\n"
print(rcptCommand)
clientSocket.send(rcptCommand.encode())
recv6 = clientSocket.recv(1024)
print(recv6.decode())
# Fill in end

# Send DATA command and print server response.
# Fill in start
print("Start send data!")
dataCommand = "DATA\r\n"
clientSocket.send(dataCommand.encode())
recv7 = clientSocket.recv(1024)
print(recv7.decode())
# Fill in end

# Send message data.
# Fill in start
clientSocket.send(msg)
# Fill in end 

# Message ends with a single period.
# Fill in start
clientSocket.send(endmsg.encode())
# Fill in end

# Send QUIT command and get server response.
# Fill in start
quitCommand = "QUIT\r\n"
clientSocket.send(quitCommand.encode())
recv8 = clientSocket.recv(1024)
print(recv8.decode())
# Fill in end

启动客户端,输入账号密码以及接收方即可在接收方邮箱收到邮件: