ESP8226 micropython ssl 与 SMTP 的连接问题

ESP8226 micropython ssl connection issue with SMTP

所以在做了很多研究之后,我已经能够使用 Google 的 gmail 服务器发送 SMTP 电子邮件。我在 Windows 和 Linux 上的 CPython 中使用相同的代码而没有问题,但是当我尝试在 ESP8266 或 Unix 端口上使用 Micropython 1.9.2(截至 9 月 11 日的最新版本)上的代码时代码锁定在第 63 行,但我不知道为什么。

任何关于如何纠正这个问题的建议将不胜感激,因为我唯一能想到的另一件事是 Micropython 的 SSL 实现是不够的,我将需要尝试移植 CPython SSL。

谢谢。

违规代码是:

61    heloCommand = 'EHLO Alice\r\n'
62    ssl_clientSocket.write(heloCommand.encode())
63    recv1 = ssl_clientSocket.read(1024)
64    print(recv1)

代码(是的,我知道它很丑,并计划在 micropython 中工作后清理它):

    # Micropython
try:
  import usocket as socket
  #import base64
  import ussl as ssl

except:
# Python version 3
  import socket
  #import base64
  import ssl


msg = """From: XXX@gmail.com
To: XXX@gmail.com
Subject: Testing

Testing transmission thru python
"""
endmsg = "\r\n.\r\n"

recipient = "XXX@gmail.com"
sender = "XXX@gmail.com"
username = "XXX@gmail.com"
password = 'Mary_Had_A_Password_of_123'

# Choose a mail server (e.g. Google mail server) and call it mailserver
mailserver = "smtp.gmail.com"
port = 587

# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket.socket()
clientSocket.connect(socket.getaddrinfo(mailserver, port)[0][-1])
recv = clientSocket.recv(1024)
print(recv)
print(recv[:3])
if recv[:3] != b'220':
    print('220 reply not received from server.')

# Send HELO command and print server response.
heloCommand = 'EHLO Alice\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024)
print(recv1)
if recv1[:3] != b'250':
    print('250 reply not received from server.')

# Request an encrypted connection
startTlsCommand = 'STARTTLS\r\n'
clientSocket.send(startTlsCommand.encode())
tls_recv = clientSocket.recv(1024)
print(tls_recv)
if tls_recv[:3] != b'220':
    print('220 reply not received from server')

# Encrypt the socket
#ssl_clientSocket = ssl.wrap_socket(clientSocket, ssl_version=ssl.PROTOCOL_TLSv1)
ssl_clientSocket = ssl.wrap_socket(clientSocket)
print("Secure socket created")

heloCommand = 'EHLO Alice\r\n'
ssl_clientSocket.write(heloCommand.encode())
recv1 = ssl_clientSocket.read(1024)
print(recv1)

# Send the AUTH LOGIN command and print server response.
authCommand = 'AUTH LOGIN\r\n'
ssl_clientSocket.write(authCommand.encode())
auth_recv = ssl_clientSocket.read(1024)
print(auth_recv)
if auth_recv[:3] != b'334':
    print('334 reply not received from server')

print("Sending username / password")
# Send username and print server response.
#uname = base64.b64encode((username).encode())
uname=b'Base64EncryptedUser=='
pword=b'Base64EncryptedPassword'
print(str(uname))
ssl_clientSocket.write(uname)
ssl_clientSocket.write('\r\n'.encode())
uname_recv = ssl_clientSocket.read(1024)
print(uname_recv)
if uname_recv[:3] != b'334':
    print('334 reply not received from server')

# Send password and print server response.
#pword = base64.b64encode((password).encode())
print(str(pword))
ssl_clientSocket.write(pword)
ssl_clientSocket.write('\r\n'.encode())
pword_recv = ssl_clientSocket.read(1024)

print(pword_recv)
if pword_recv[:3] != b'235':
    print('235 reply not received from server')

# Send MAIL FROM command and print server response.
mailFromCommand = 'MAIL FROM: <' + sender + '>\r\n'
ssl_clientSocket.write(mailFromCommand.encode())
recv2 = ssl_clientSocket.read(1024)
print(recv2)
if recv2[:3] != b'250':
    print('250 reply not received from server.')

# Send RCPT TO command and print server response.
rcptToCommand = 'RCPT TO: <' + recipient + '>\r\n'
ssl_clientSocket.write(rcptToCommand.encode())
recv3 = ssl_clientSocket.read(1024)
print(recv3)
if recv3[:3] != b'250':
    print('250 reply not received from server.')

# Send DATA command and print server response.
dataCommand = 'DATA\r\n'
ssl_clientSocket.write(dataCommand.encode())
recv4 = ssl_clientSocket.read(1024)
print(recv4)
if recv4[:3] != b'354':
    print('354 reply not received from server.')

# Send message data.
ssl_clientSocket.write(msg.encode())

# Message ends with a single period.
ssl_clientSocket.write(endmsg.encode())
recv5 = ssl_clientSocket.read(1024)
print(recv5)
if recv5[:3] != b'250':
    print('250 reply not received from server.')

# Send QUIT command and get server response.
quitCommand = 'QUIT\r\n'
ssl_clientSocket.write(quitCommand.encode())
recv6 = ssl_clientSocket.read(1024)
print(recv6)
if recv6[:3] != b'221':
    print('221 reply not received from server.')

clientSocket.close()

已接受答案的更新代码(是的,我知道它很丑陋),其中 read(1024) 被 readline() 替换为 ssl 套接字。还需要添加一种方法来在 ssl EHLO 命令之后清除缓冲区,因此在第一个 EHLO 中添加了一个 "recvCount=recv1.decode().count('\n')",然后在 ssl EHLO 中添加了一个循环以获得相同的计数:

    # Micropython
try:
  import usocket as socket
  #import base64
  import ussl as ssl

except:
# Python version 3
  import socket
  #import base64
  import ssl


msg = """From: XXX@gmail.com
To: XXX@gmail.com
Subject: Testing

Testing transmission thru python
"""
endmsg = "\r\n.\r\n"

recipient = "XXX@gmail.com"
sender = "XXX@gmail.com"
username = "XXX@gmail.com"
password = 'Mary_Had_A_Password_of_123'

# Choose a mail server (e.g. Google mail server) and call it mailserver
mailserver = "smtp.gmail.com"
port = 587

# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket.socket()
clientSocket.connect(socket.getaddrinfo(mailserver, port)[0][-1])
recv = clientSocket.recv(1024)
print(recv)
print(recv[:3])
if recv[:3] != b'220':
    print('220 reply not received from server.')

# Send HELO command and print server response.
heloCommand = 'EHLO Alice\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024)
recvCount=recv1.decode().count('\n')
print(recv1)
if recv1[:3] != b'250':
    print('250 reply not received from server.')

# Request an encrypted connection
startTlsCommand = 'STARTTLS\r\n'
clientSocket.send(startTlsCommand.encode())
tls_recv = clientSocket.recv(1024)
print(tls_recv)
if tls_recv[:3] != b'220':
    print('220 reply not received from server')

# Encrypt the socket
#ssl_clientSocket = ssl.wrap_socket(clientSocket, ssl_version=ssl.PROTOCOL_TLSv1)
ssl_clientSocket = ssl.wrap_socket(clientSocket)
print("Secure socket created")

heloCommand = 'EHLO Alice\r\n'
ssl_clientSocket.write(heloCommand.encode())
recv1=''
for index in range(0,recvCount):
  recv1 = recv1+ssl_clientSocket.readline().decode()
print(recv1)

# Send the AUTH LOGIN command and print server response.
authCommand = 'AUTH LOGIN\r\n'
ssl_clientSocket.write(authCommand.encode())
auth_recv = ssl_clientSocket.readline()
print(auth_recv)
if auth_recv[:3] != b'334':
    print('334 reply not received from server')

print("Sending username / password")
# Send username and print server response.
#uname = base64.b64encode((username).encode())
uname=b'Base64EncryptedUser=='
pword=b'Base64EncryptedPassword'
print(str(uname))
ssl_clientSocket.write(uname)
ssl_clientSocket.write('\r\n'.encode())
uname_recv = ssl_clientSocket.readline()
print(uname_recv)
if uname_recv[:3] != b'334':
    print('334 reply not received from server')

# Send password and print server response.
#pword = base64.b64encode((password).encode())
print(str(pword))
ssl_clientSocket.write(pword)
ssl_clientSocket.write('\r\n'.encode())
pword_recv = ssl_clientSocket.readline()

print(pword_recv)
if pword_recv[:3] != b'235':
    print('235 reply not received from server')

# Send MAIL FROM command and print server response.
mailFromCommand = 'MAIL FROM: <' + sender + '>\r\n'
ssl_clientSocket.write(mailFromCommand.encode())
recv2 = ssl_clientSocket.readline()
print(recv2)
if recv2[:3] != b'250':
    print('250 reply not received from server.')

# Send RCPT TO command and print server response.
rcptToCommand = 'RCPT TO: <' + recipient + '>\r\n'
ssl_clientSocket.write(rcptToCommand.encode())
recv3 = ssl_clientSocket.readline()
print(recv3)
if recv3[:3] != b'250':
    print('250 reply not received from server.')

# Send DATA command and print server response.
dataCommand = 'DATA\r\n'
ssl_clientSocket.write(dataCommand.encode())
recv4 = ssl_clientSocket.readline()
print(recv4)
if recv4[:3] != b'354':
    print('354 reply not received from server.')

# Send message data.
ssl_clientSocket.write(msg.encode())

# Message ends with a single period.
ssl_clientSocket.write(endmsg.encode())
recv5 = ssl_clientSocket.readline()
print(recv5)
if recv5[:3] != b'250':
    print('250 reply not received from server.')

# Send QUIT command and get server response.
quitCommand = 'QUIT\r\n'
ssl_clientSocket.write(quitCommand.encode())
recv6 = ssl_clientSocket.readline()
print(recv6)
if recv6[:3] != b'221':
    print('221 reply not received from server.')

clientSocket.close()

recv1 = ssl_clientSocket.read(1024)

请阅读有关 MicroPython 流语义的信息(它与 Python 流语义非常匹配,只是做了一些简化): http://docs.micropython.org/en/latest/pyboard/library/uio.html?#conceptual-hierarchy

引用的语句所做的是恰好请求 1024 条数据(MicroPython 默认遵循 "buffered stream" Python 语义)。如果没有那么多数据,.read() 将耐心等待直到足够的数据到达(或直到 EOF 或错误发生)。

SMTP 协议是面向线路的,因此您需要使用 .readline()