通过完成本实验,您将更加了解SMTP协议。您还将学到使用Python实现标准协议的经验。

您的任务是开发一个简单的邮件客户端,将邮件发送给任意收件人。您的客户端将需要连接到邮件服务器,使用SMTP协议与邮件服务器进行对话,并向邮件服务器发送电子邮件。 Python提供了一个名为smtplib的模块,它内置了使用SMTP协议发送邮件的方法。但是我们不会在本实验中使用此模块,因为它隐藏了SMTP和套接字编程的细节。

为了限制垃圾邮件,一些邮件服务器不接受来源随意的TCP连接。对于下面所述的实验,您可能需要尝试连接到您的大学邮件服务器和流行的Webmail服务器(如AOL邮件服务器)。您也可以尝试从您的家和您的大学校园进行连接。

代码

下面你会找到客户端的代码框架。您将完成代码框架。您需要填写代码的地方标有#Fill in start和#Fill in end。每个地方都可能需要不止一行代码。

附加说明

在某些情况下,接收邮件服务器可能会将您的电子邮件分类为垃圾邮件。当您查找从客户端发送的电子邮件时,请检查垃圾邮件文件夹。

要上交的内容

在您的上交内容中,你需要提供完整的SMTP邮件客户端的代码以及一张能显示您确实收到电子邮件的屏幕截图。

邮件客户端的Python代码框架

  1. from socket import *
  2. msg = "\r\n I love computer networks!"
  3. endmsg = "\r\n.\r\n"
  4. # Choose a mail server (e.g. Google mail server) and call it mailserver
  5. mailserver = #Fill in start #Fill in end
  6. # Create socket called clientSocket and establish a TCP connection with mailserver
  7. #Fill in start
  8. #Fill in end
  9. recv = clientSocket.recv(1024)
  10. print recv
  11. if recv[:3] != '220':
  12. print '220 reply not received from server.'
  13. # Send HELO command and print server response.
  14. heloCommand = 'HELO Alice\r\n'
  15. clientSocket.send(heloCommand)
  16. recv1 = clientSocket.recv(1024)
  17. print recv1
  18. if recv1[:3] != '250':
  19. print '250 reply not received from server.'
  20. # Send MAIL FROM command and print server response.
  21. # Fill in start
  22. # Fill in end
  23. # Send RCPT TO command and print server response.
  24. # Fill in start
  25. # Fill in end
  26. # Send DATA command and print server response.
  27. # Fill in start
  28. # Fill in end
  29. # Send message data.
  30. # Fill in start
  31. # Fill in end
  32. # Message ends with a single period.
  33. # Fill in start
  34. # Fill in end
  35. # Send QUIT command and get server response.
  36. # Fill in start
  37. # Fill in end

可选练习

  1. 类似Google邮件的服务器(如地址:smtp.gmail.com,端口:587))要求您的客户端在发送MAIL FROM命令之前,需要为了身份验证和安全原因添加传输层安全(TLS)或安全套接字层(SSL)。将TLS / SSL命令添加到现有的命令中,并使用上述地址和端口为Google邮件服务器实现客户端。
  2. 您当前的SMTP邮件客户端只能在电子邮件正文中发送文本消息。修改您的客户端,使其可以发送包含文本和图像的电子邮件。

    答案

    作业3答案

    ```python

    改为Python3格式

    from socket import * import base64

    Choose a mail server (e.g. Google mail server) and call it mailserver

    mailserver = “smtp.163.com” mailUser = ‘jz163wy’ mailFromAddress = ‘jz163wy@163.com’ mailPassWord = ‘**‘ mailToAddress = ‘ecnujz@qq.com’

msg = ‘FROM: ‘ + mailFromAddress + ‘\r\n’ msg += ‘TO: ‘ + mailToAddress + ‘\r\n’ msg += ‘Subject: ‘ + ‘test’ + ‘\r\n’ msg += “\r\n I love computer networks!” endmsg = “\r\n.\r\n”

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) recv = recv.decode() print(recv) if recv[:3] != ‘220’: print(‘220 reply not received from server.’)

Send HELO command and print server response.

heloCommand = ‘HELO mailserver\r\n’ while True: clientSocket.send(heloCommand.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘250’: break

登录过程

loginCommand = ‘auth login\r\n’ while True: clientSocket.send(loginCommand.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘334’: break

邮箱账户经过base64编码

userCommand = base64.b64encode(mailUser.encode()) + b’\r\n’ while True: clientSocket.send(userCommand) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘334’: break

邮箱密码经过base64编码 这里不展示密码了

passCommand = base64.b64encode(mailPassWord.encode()) + b’\r\n’ while True: clientSocket.send(passCommand) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘235’: break

Send MAIL FROM command and print server response.

MFCommand = ‘MAIL FROM: <’+ mailFromAddress + ‘>\r\n’ while True: clientSocket.send(MFCommand.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘250’: break

Send RCPT TO command and print server response.

RTCommand = ‘RCPT TO: <’+ mailToAddress + ‘>\r\n’ while True: clientSocket.send(RTCommand.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘250’: break

Send DATA command and print server response.

DATACommand = ‘DATA\r\n’ while True: clientSocket.send(DATACommand.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘354’: break

Send message data.

clientSocket.send(msg.encode())

Message ends with a single period.

while True: clientSocket.send(endmsg.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘250’: break

Send QUIT command and get server response.

QUITCommand = ‘QUIT\r\n’ while True: clientSocket.send(QUITCommand.encode()) recv = clientSocket.recv(1024) recv = recv.decode() print(recv) if recv[:3] == ‘221’: break

  1. <a name="PmTCl"></a>
  2. ## 可选练习1答案
  3. ```python
  4. #改为Python3格式
  5. from socket import *
  6. import sys
  7. import os
  8. if len(sys.argv) <= 1:
  9. print('Usage : "python ProxyServer.py server_ip"\n[server_ip : It is the IP Address Of Proxy Server')
  10. sys.exit(2)
  11. # Create a server socket, bind it to a port and start listening
  12. tcpSerSock = socket(AF_INET, SOCK_STREAM)
  13. tcpSerPort = int(sys.argv[1])
  14. tcpSerSock.bind(("", tcpSerPort))
  15. print(tcpSerPort)
  16. tcpSerSock.listen(10)
  17. while 1:
  18. # Strat receiving data from the client
  19. print('Ready to serve...')
  20. tcpCliSock, addr = tcpSerSock.accept()
  21. print('Received a connection from:', addr)
  22. message = tcpCliSock.recv(1024)
  23. message = message.decode()
  24. print("message:", message)
  25. if(message == ''):
  26. continue
  27. # Extract the filename from the given message
  28. print("message.split()[1]:", message.split()[1])
  29. filename = message.split()[1].partition("/")[2]
  30. print("filename:", filename)
  31. fileExist = "false"
  32. filetouse = "/" + filename
  33. print("filetouse:", filetouse)
  34. try:
  35. # Check wether the file exist in the cache
  36. f = open("WEB/" + filetouse[1:], "rb")
  37. outputdata = f.read()
  38. f.close()
  39. fileExist = "true"
  40. # ProxyServer finds a cache hit and generates a response message
  41. tcpCliSock.send("HTTP/1.1 200 OK\r\n".encode())
  42. tcpCliSock.send("Content-Type:text/html\r\n\r\n".encode())
  43. tcpCliSock.send(outputdata)
  44. print('Read from cache')
  45. # Error handling for file not found in cache
  46. except IOError:
  47. if fileExist == "false":
  48. # Create a socket on the proxyserver
  49. c = socket(AF_INET, SOCK_STREAM)
  50. hostn = filename.replace("www.","",1)
  51. print("hostn:", hostn)
  52. try:
  53. # Connect to the socket to port 80
  54. serverName = hostn.partition("/")[0]
  55. serverPort = 80
  56. print((serverName, serverPort))
  57. c.connect((serverName, serverPort))
  58. askFile = ''.join(filename.partition('/')[1:])
  59. print("askFile:", askFile)
  60. # Create a temporary file on this socket and ask port 80
  61. # for the file requested by the client
  62. fileobj = c.makefile('rwb', 0)
  63. fileobj.write("GET ".encode() + askFile.encode() + " HTTP/1.0\r\nHost: ".encode() + serverName.encode() + "\r\n\r\n".encode())
  64. # Read the response into buffer
  65. serverResponse = fileobj.read()
  66. if serverResponse.split()[0] != b'404':
  67. print('404')
  68. tcpCliSock.send("HTTP/1.1 404 Not Found\r\n\r\n".encode())
  69. tcpCliSock.close()
  70. continue
  71. # Create a new file in the cache for the requested file.
  72. # Also send the response in the buffer to client socket and the corresponding file in the cache
  73. filename = "WEB/" + filename
  74. filesplit = filename.split('/')
  75. for i in range(0, len(filesplit) - 1):
  76. if not os.path.exists("/".join(filesplit[0:i+1])):
  77. os.makedirs("/".join(filesplit[0:i+1]))
  78. tmpFile = open(filename, "wb")
  79. print(serverResponse)
  80. serverResponse = serverResponse.split(b'\r\n\r\n')[1]
  81. print(serverResponse)
  82. tmpFile.write(serverResponse)
  83. tmpFile.close()
  84. tcpCliSock.send("HTTP/1.1 200 OK\r\n".encode())
  85. tcpCliSock.send("Content-Type:text/html\r\n\r\n".encode())
  86. tcpCliSock.send(serverResponse)
  87. except:
  88. print("Illegal request")
  89. c.close()
  90. else:
  91. # HTTP response message for file not found
  92. print("NET ERROR")
  93. # Close the client and the server sockets
  94. tcpCliSock.close()
  95. tcpSerSock.close()

可选练习2答案

  1. #改为Python3格式
  2. from socket import *
  3. import base64
  4. import ssl
  5. # Choose a mail server (e.g. Google mail server) and call it mailserver
  6. mailserver = "smtp.163.com"
  7. mailUser = 'jz163wy'
  8. mailFromAddress = 'jz163wy@163.com'
  9. mailPassWord = '******'
  10. mailToAddress = 'ecnujz@qq.com'
  11. # transfer image and html
  12. with open("timg.jpg","rb") as f:
  13. image_data = base64.b64encode(f.read())
  14. with open("hello.html","rb") as f:
  15. html_data = base64.b64encode(f.read())
  16. # 构造邮件正文
  17. msg = 'FROM: ' + mailFromAddress + '\r\n'
  18. msg += 'TO: ' + mailToAddress + '\r\n'
  19. msg += 'Subject: ' + 'text and picture' + '\r\n'
  20. msg += 'Content-Type:multipart/related; boundary="----=_NextPart_000_0012345JZ"\r\n'
  21. msg += 'MIME-Version: 1.0\r\n'
  22. msg += '\r\n'
  23. msg = msg.encode()
  24. msg += '------=_NextPart_000_0012345JZ\r\n'.encode()
  25. msg += 'Content-Type: text/html; charset=UTF-8\r\n'.encode()
  26. msg += 'Content-Transfer-Encoding: base64\r\n'.encode()
  27. msg += '\r\n'.encode()
  28. msg += html_data
  29. msg += '\r\n'.encode()
  30. msg += '\r\n'.encode()
  31. msg += '------=_NextPart_000_0012345JZ\r\n'.encode()
  32. msg += 'Content-Type: image/jpeg; name="timg.jpg"\r\n'.encode()
  33. msg += 'Content-Transfer-Encoding: base64\r\n'.encode()
  34. msg += 'Content-ID: JZJZJZJZJZJZJZJZ'.encode()
  35. msg += '\r\n'.encode()
  36. msg += image_data + "\r\n".encode()
  37. msg += '\r\n'.encode()
  38. msg += '------=_NextPart_000_0012345JZ--\r\n'.encode()
  39. endmsg = "\r\n.\r\n"
  40. # Create socket called clientSocket and establish a TCP connection with mailserver
  41. context = ssl.create_default_context()
  42. clientSocket = socket(AF_INET, SOCK_STREAM)
  43. clientSocket.connect((mailserver, 465))
  44. clientSocketSSL = context.wrap_socket(clientSocket, server_hostname=mailserver)
  45. recv = clientSocketSSL.recv(1024)
  46. recv = recv.decode()
  47. print(recv)
  48. if recv[:3] != '220':
  49. print('220 reply not received from server.')
  50. # Send HELO command and print server response.
  51. heloCommand = 'HELO mailserver\r\n'
  52. while True:
  53. clientSocketSSL.send(heloCommand.encode())
  54. recv = clientSocketSSL.recv(1024)
  55. recv = recv.decode()
  56. print(recv)
  57. if recv[:3] == '250':
  58. break
  59. # 登录过程
  60. loginCommand = 'auth login\r\n'
  61. while True:
  62. clientSocketSSL.send(loginCommand.encode())
  63. recv = clientSocketSSL.recv(1024)
  64. recv = recv.decode()
  65. print(recv)
  66. if recv[:3] == '334':
  67. break
  68. # 邮箱账户经过base64编码
  69. userCommand = base64.b64encode(mailUser.encode()) + b'\r\n'
  70. while True:
  71. clientSocketSSL.send(userCommand)
  72. recv = clientSocketSSL.recv(1024)
  73. recv = recv.decode()
  74. print(recv)
  75. if recv[:3] == '334':
  76. break
  77. # 邮箱密码经过base64编码 这里不展示密码了
  78. passCommand = base64.b64encode(mailPassWord.encode()) + b'\r\n'
  79. while True:
  80. clientSocketSSL.send(passCommand)
  81. recv = clientSocketSSL.recv(1024)
  82. recv = recv.decode()
  83. print(recv)
  84. if recv[:3] == '235':
  85. break
  86. # Send MAIL FROM command and print server response.
  87. MFCommand = 'MAIL FROM: <'+ mailFromAddress + '>\r\n'
  88. while True:
  89. clientSocketSSL.send(MFCommand.encode())
  90. recv = clientSocketSSL.recv(1024)
  91. recv = recv.decode()
  92. print(recv)
  93. if recv[:3] == '250':
  94. break
  95. # Send RCPT TO command and print server response.
  96. RTCommand = 'RCPT TO: <'+ mailToAddress + '>\r\n'
  97. while True:
  98. clientSocketSSL.send(RTCommand.encode())
  99. recv = clientSocketSSL.recv(1024)
  100. recv = recv.decode()
  101. print(recv)
  102. if recv[:3] == '250':
  103. break
  104. # Send DATA command and print server response.
  105. DATACommand = 'DATA\r\n'
  106. while True:
  107. clientSocketSSL.send(DATACommand.encode())
  108. recv = clientSocketSSL.recv(1024)
  109. recv = recv.decode()
  110. print(recv)
  111. if recv[:3] == '354':
  112. break
  113. # Send message data.
  114. clientSocketSSL.send(msg)
  115. # Message ends with a single period.
  116. while True:
  117. clientSocketSSL.send(endmsg.encode())
  118. recv = clientSocketSSL.recv(1024)
  119. recv = recv.decode()
  120. print(recv)
  121. if recv[:3] == '250':
  122. break
  123. # Send QUIT command and get server response.
  124. QUITCommand = 'QUIT\r\n'
  125. while True:
  126. clientSocketSSL.send(QUITCommand.encode())
  127. recv = clientSocketSSL.recv(1024)
  128. recv = recv.decode()
  129. print(recv)
  130. if recv[:3] == '221':
  131. break
  132. clientSocketSSL.close()