上手就是第一个坑。该书第二章以及该题的代码模板中皆没有提及授权登录的问题,实际上目前国内的email提供商(腾讯、163等)对于第三方登录都采取授权登录的方式(gmail因为一些众所周知的原因没试过),如果直接按照书中方式发送邮件会出现如下错误。
实际上我们可以将书上示例的smtp握手过程扩充成如下所示。
S: 220 xxx.com C: HELO yourname S: 250 greeting contents C: AUTH LOGIN S: 334 dXNlcm5hbWU6 #username C: <your_username_in_base64> S: 334 UGFzc3dvcmQ6 #password C: <your_password_in_base64> S: 235 AUTH successful C: MAIL FROM: <from_mail_address> S: 250 OK C: RCPT TO: <to_mail_address> S: 250 OK C: DATA S: 354 End data with <CR><LF>.<CR><LF> C: main body of the message ... ... C: . S: 250 Mail accepted C: QUIT S: Bye
其中‘your_username_in_base64’和'your_password_in_base64'分别是你的邮箱用户名和授权码的base64编码,授权码可以从邮箱提供商获得,具体方法请百度。
看到这里,如果你和我一样迫不及待地去telnet一下smtp.163.com,尝试用上述的命令手工发送一封邮件的话,那你就会踩进第二个坑里。

好吧这个问题我花了两天,直到现在也没找出原因,实际上我后来尝试自己抓了一份outlook发送邮件的完整过程的包。
可以看到与我贴出的smtp握手过程基本一致,并且同样是输入我的邮箱地址的base64编码,得到的却是334肯定的回答,最终成功将邮件发出。初步猜测是windows键盘输入的问题。
这题最终的代码如下,这个smtp客户端能够正常工作,而不会像手动telnet那样报错。
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 = 'smtp.163.com' 6 serverPort = 25 7 # Create socket called clientSocket and establish a TCP connection with mailserver 8 fromaddress = 'decayfang@163.com' 9 toaddress = '1186565149@qq.com' 10 11 username = "ZGVjYXlmYW5nQDE2My5jb20=" 12 password = "xxxxxxxxxxxxxxxxxxxxxxxx" 13 14 clientSocket = socket(AF_INET, SOCK_STREAM) 15 clientSocket.connect((mailserver, serverPort)) 16 17 recv = clientSocket.recv(1024).decode() 18 print(recv) 19 if recv[:3] != '220': 20 print('220 reply not received from server.') 21 22 # Send HELO command and print server response. 23 heloCommand = 'HELO Alice\r\n' 24 clientSocket.send(heloCommand.encode()) 25 recv1 = clientSocket.recv(1024).decode() 26 print(recv1) 27 if recv1[:3] != '250': 28 print('250 reply not received from server.') 29 30 authCommand = 'AUTH LOGIN\r\n' 31 clientSocket.send(authCommand.encode()) 32 recv = clientSocket.recv(1024).decode() 33 print(recv) 34 if recv[:3] != '334': 35 print('334 reply not received from server.') 36 37 clientSocket.send((username + '\r\n').encode()) 38 recv = clientSocket.recv(1024).decode() 39 print(recv) 40 if recv[:3] != '334': 41 print('334 reply not received from server.') 42 43 clientSocket.send((password + '\r\n').encode()) 44 recv = clientSocket.recv(1024).decode() 45 print(recv) 46 if recv[:3] != '235': 47 print('235 reply not received from server.') 48 49 # Send MAIL FROM command and print server response. 50 # Fill in start 51 clientSocket.send(('MAIL FROM: <' + fromaddress + '>\r\n').encode()) 52 recv = clientSocket.recv(1024).decode() 53 print(recv) 54 if recv[:3] != '250': 55 print('250 reply not received from server.') 56 # Fill in end 57 58 # Send RCPT TO command and print server response. 59 # Fill in start 60 clientSocket.send(('RCPT TO: <' + toaddress + '>\r\n').encode()) 61 recv = clientSocket.recv(1024).decode() 62 print(recv) 63 if recv[:3] != '250': 64 print('250 reply not received from server.') 65 # Fill in end 66 67 # Send DATA command and print server response. 68 # Fill in start 69 clientSocket.send('DATA\r\n'.encode()) 70 recv = clientSocket.recv(1024).decode() 71 print(recv) 72 if recv[:3] != '354': 73 print('354 reply not received from server.') 74 # Fill in end 75 76 # Send message data. 77 # Fill in start 78 message = 'From: ' + fromaddress + '\r\n' 79 message += 'To: ' + toaddress + '\r\n' 80 message += 'Subject: TESTING MAIL\r\n' 81 message += msg + '\r\n' 82 clientSocket.send(message.encode()) 83 # Fill in end 84 85 # Message ends with a single period. 86 # Fill in start 87 clientSocket.send(endmsg.encode()) 88 recv = clientSocket.recv(1024).decode() 89 print(recv) 90 if recv[:3] != '250': 91 print('250 reply not received from server.') 92 # Fill in end 93 94 # Send QUIT command and get server response. 95 # Fill in start 96 clientSocket.send('QUIT'.encode()) 97 # Fill in end 98 99 input('Press <Enter>')

浙公网安备 33010602011771号