zoukankan      html  css  js  c++  java
  • 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”,并结束会话

    请添加图片描述

  • 相关阅读:
    一款标注颜色,距离的小软件 markman
    EF5+MVC4系列(7) 后台SelectListItem传值给前台显示Select下拉框;后台Action接收浏览器传值的4种方式; 后台Action向前台View视图传递数据的四种方式(ViewDate,TempDate,ViewBag,Model (实际是ViewDate.Model传值))
    ISAPI和CGI限制中没有ASP.NET v4.0 ; vS2013检测到在集成的托管管道模式下不适用的 ASP.NET 设置。
    EF5+MVC4系列(6) 简单三层的搭配(泛型) 实现 增删改查
    MVC4小细节
    目标和制定周期问题
    转 : 设计实例对比:MySQL vs MongoDB
    转:C#通过WebClient/HttpWebRequest实现http的post/get方法
    如何使用 OpenFileDialog 组件 (选择文件组件)
    resharper安装后,F12不能转到定义,也不是反编译,而是转到对象浏览器(object browser)
  • 原文地址:https://www.cnblogs.com/most-silence/p/15495329.html
Copyright © 2011-2022 走看看