• SMTP协议(Python实现)


    SMTP介绍

    过多基础概念我就不讲了, 直接翻书或者维基百科,
    简单来说SMTP就是简单邮件传输协议, 既然是传输协议就是发邮件收邮件, 两者都有.
    POP3IMAP区别开来
    从这里简单可以看出区别
    在这里插入图片描述

    • 工作过程
      其实就是收发邮件的双方都找了中间代理. SMTP是建立在TCP连接且端口为25的应用层协议. 主要是非实时的, 因为有的客户只会定期(15min)去询问代理邮箱有没有新的邮件
      在这里插入图片描述
      这里就不进行文字赘述了, 等下实操代码用图片来分析流程

    SMTP命令和应答代码

    应答代码, 服务器的返回消息

    代码说明解决方案
    220一般建立连接后就返回这个消息
    250完成一个请求操作/命令后返回的消息
    354邮件具体内容的传输说明
    503命令顺序错误了/需要授权一般先检查几个关键命令的顺序,再检查授权/登录错误
    502命令没有实现
    命令参数
    HELO主机名/对方名字
    MAIL FROM自己邮箱
    RCPT TO对方邮箱
    DATA开始传输数据, 邮件的主体
    AUTH LOGIN授权 一般就直接用LOGIN登录

    邮件的报文格式

    RFC文档的对报文格式的定义

    1. 所有报文都是由ASCII码组成
    2. 报文由报文行组成,各行之间用回车(CR)、换行(LF)符分隔
    3. 报文的长度不能超过998个字符
    4. 报文行的长度≤78个字符之内(不包括回车换行符)
    5. 报文中可包括多个首部字段和首部内容
    6. 报文可包括一个主体,主体必须用一个空行与其首部分隔
    7. 除非需要使用回车与换行符,否则报文中不使用回车与换行符
      https://img-blog.csdn.net/20170403164927322?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU2NDQyMzQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

    具体传输过程

    先给代码和wireshark

    from socket import *
    import base64
    
    # Mail content
    subject = "I love computer networks!"
    contenttype = "text/plain"
    msg = "I love computer networks!"
    endmsg = "
    .
    "
    
    # Choose a mail server (e.g. Google mail server) and call it mailserver 
    # 不清楚的直接搜大学/企业邮箱服务器即可, 大部分都是25端口的发送端口
    mailserver = "***.mail"
    
    # Sender and reciever
    fromaddress = "****@mail"
    toaddress = "****@qq.com"
    
    # Auth information (Encode with base64)
    username = base64.b64encode(fromaddress.encode()).decode()
    password = base64.b64encode("******".encode()).decode()
    
    
    # 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).decode()
    print(recv)
    if recv[:3] != '220':
        print('220 reply not received from server.')
    
    # Send HELO command and print server response.
    heloCommand = 'HELO Alice
    '
    clientSocket.send(heloCommand.encode())
    recv1 = clientSocket.recv(1024).decode()
    print(recv1)
    if recv1[:3] != '250':
        print('250 reply not received from server.')
    
    
    # Auth必须在hello之后再进行授权
    clientSocket.sendall('AUTH LOGIN
    '.encode())
    recv = clientSocket.recv(1024).decode()
    print(recv)
    if (recv[:3] != '334'):
    	print('334 reply not received from server')
    
    clientSocket.sendall((username + '
    ').encode())
    recv = clientSocket.recv(1024).decode()
    print(recv)
    if (recv[:3] != '334'):
    	print('334 reply not received from server')
    
    clientSocket.sendall((password + '
    ').encode())
    recv = clientSocket.recv(1024).decode()
    print(recv)
    if (recv[:3] != '235'):
    	print('235 reply not received from server')
    
    # Send MAIL FROM command and print server response.
    # Fill in start
    #sendall将发送全部数据直到错误或者全部发送完, send发送少于所需要的字节数
    clientSocket.sendall(('MAIL FROM: <'+fromaddress+'>
    ').encode())
    recv2 = clientSocket.recv(1024).decode()
    print(recv2)
    if (recv2[:3] != '250'):
        print('250 reply not received from server.')
    # Fill in end
    
    # Send RCPT TO command and print server response. 
    # Fill in start 这里的命令错了 不是MAIL是RCPT
    clientSocket.sendall(('RCPT TO: <'+toaddress+'>
    ').encode())
    recv3 = clientSocket.recv(1024).decode()
    print(recv3)
    if (recv3[:3] != '250'):
        print('250 reply not received from server.')
    
    # Fill in end
    # Send DATA command and print server response. 
    # Fill in start
    clientSocket.send(('DATA
    ').encode())
    recv = clientSocket.recv(1024).decode()
    print(recv)
    if (recv[:3] != '354'):
        print('354 reply not received from server')
    # Fill in end
    
    # Send message data.
    # Fill in start
    message = 'from:' + fromaddress + '
    '
    message += 'to:' + toaddress + '
    '
    message += 'subject:' + subject + '
    '
    message += 'Content-Type:' + contenttype + '
    '
    message += '
    ' + msg
    clientSocket.sendall(message.encode())
    
    # Fill in end
    # Message ends with a single period.
    # Fill in start
    clientSocket.sendall(endmsg.encode())
    recv = clientSocket.recv(1024).decode()
    print(recv)
    if (recv[:3] != '250'):
    	print('250 reply not received from server')
    # Fill in end
    # Send QUIT command and get server response.
    # Fill in start
    clientSocket.sendall('QUIT
    '.encode())
    # Fill in end
    
    clientSocket.close()
    

    在这里插入图片描述

    先建立连接

    先用socket建立TCP连接

    使用邮件服务器和端口号进行握手, 建立TCP连接, 服务器返回220
    客户端接收到220之后 发送HELO命令, 发起会话
    服务器返回250, 很OK

    连接建立后一般要登录了,

    因为不是自己建立的邮件服务器, 是企业/别人的, 需要授权登入

    客户端发送命令AUTH LOGIN 我要登录了
    服务器回答 334 dXNlcm5hbWU6 意思是 输入 username: 后者是base64加密编码
    客户发送加密后的账户名和密码
    服务器回答 235 授权成功

    发送邮件

    1. 客户用“MAIL FROM”向服务器报告发信人的邮箱与域名
    2. 服务器向客户回应应答码“250”,代表请求命令完成
    3. 客户用“RCPT TO”命令向服务器报告收信人的邮箱与域名
    4. 服务器向客户回应应答码“250”,代表请求命令完成
    5. 客户用“DTAT”命令对报文的传送进行初始化
    6. 服务器回应“354”,表示可以进行邮件输入了
    7. 客户用连续的行向服务器传送报文的内容,每行以两字符的行结束标识(CR与LF)终止。报文以只有一个“.”的行结束
    8. 服务器向客户回应应答码“250”,代表请求命令完成

    连接终止

    • 客户端发送“QUIT”命令
    • 服务器收到命令后,回应应答码“221”,并结束会话

    请添加图片描述

  • 相关阅读:
    数组方法延伸
    Vue基础知识之过滤器(四)
    Vue基础知识之vue-resource和axios(三)
    【正则】对RegExp执行typeof运算的结果
    【正则】所有的字符(总)
    学习正则有感by魔芋(命名问题)
    关于正则表达式(转)
    【15】ES6 for Humans: The Latest Standard of JavaScript: ES2015 and Beyond
    中国首届CSS开发者大会讲师照片
    拥有强大的自我驱动力是一种怎样的体验?
  • 原文地址:https://www.cnblogs.com/most-silence/p/15495329.html
Copyright © 2020-2023  润新知