当前位置:   article > 正文

Windows 环境下用 smtp 实现 Email-计算机网络_windows 环境下用 smtp 实现 email 客户端

windows 环境下用 smtp 实现 email 客户端

  • 实验目的

运用各种编程语言实现基于 smtp 协议的 Email 客户端软件。通过本实验,使学生能够对网络编程有进一步的理解和掌握,并能够理解 smtp 协议的细节。

  • 实验内容

一、选择合适的编程语言编程实现基于 smtp 协议的 Email 客户端软件。

二、安装 Email 服务器或选择已有的 Email 服务器,验证自己的 Email 客户端软件是否能进行正常的 Email 收发功能。

  • 实验结果
  1. 实验过程:
    • 获得发送邮件方的授权码

设置qq邮箱,开启POP3/SMTP服务,获得授权码

    • 编写基于smtp的邮件发送客户端

1、利用socket库和对应的smtp服务器产生TCP连接,接收到来自smtp服务器响应的220消息。

2、借助socket依次发送HELO(身份确认)、AUTH LOGIN(授权登录)、MAIL FROM:<fromaddress>、RCPT TO:<toaddress>(邮件地址填写)。

3、按照格式填写DATA内容,指定content-type以及其他header,通过socket发送给smtp服务器,smtp服务器再转接给目标地址的服务器。发送邮件完毕。

       在利用socket进行请求和响应的时候,对于不同的命令smtp服务器会响应不同的状态码:

       建立TCP连接成功:220

       HELO:250

       AUTH LOGIN: 334

       输入用户名:334

       输入密码:235

       MAIL FROM<fromaddress>: 250

RCTP TO<toaddress>: 250

DATA: 354

发送DATA: 250

可以通过检查步骤的状态码是否成功匹配,来判断本步骤是否成功进行。

③编写基于pop3的邮件接收客户端(在查询相关资料后,pop3相较于smtp更常用于邮件接收,此处选择pop3来实现更贴近实际情况)

利用python中poplib以及email库中的parser解码器来完成邮件的接收:

       1、利用poplib.POP3(ADDR,PORT)和目标pop服务器建立连接,并利用user和pass_方法来完成身份认证。

       2、利用retr方法获取对应index的邮件信息,并利用Parser().parserstr(),将获取的字符流message转换划分成dictionary对象。

       3、解码dictionary对象,对于from、to的内容,利用parseraddr进行解析地址,划分得到header和address,对于header,再利用decode_str()进行解码,而对于subject,则直接使用decode_str()进行解码,decode_str()需要依赖于decode_header。

       4、解码邮件的body部分,首先进行multipart的判断,如果是,则进行递归调用,进入到body的子部分进行解码分析,如果不是,则获取邮件的content-type,并根据其对应结果进行text和attachment的划分,将其结果打印输出。

       5、用户表示查询完毕,服务器连接中断。

  1. 代码与注释:
  • smtp邮件发送客户端

见附件smtp/ smtp_server.py

  1. import socket  
  2.   
  3. HOST = "smtp.qq.com"  
  4. PORT = 587  
  5.   
  6. class smpt_client():  
  7.     endMsg = "\r\n.\r\n"  
  8.     def __init__(self,username,password,fromAddress,toAddress,subject, msg):  
  9.         self.username = username  
  10.         self.password = password  
  11.         # 用户名和密码  
  12.         self.fromAddress = fromAddress  
  13.         self.toAddress = toAddress  
  14.   
  15.         self.msg = '\r\n' + msg + '\r\n'  
  16.         self.subject = subject  
  17.   
  18.     def push(self,connection):  
  19.         receive_state = connection.recv(1024).decode()  
  20.         # 获取连接状态  
  21.         print(receive_state)  
  22.         # 220 server address.  
  23.         if receive_state[:3]!='220':  
  24.             print("request error.\n")  
  25.   
  26.         HELO_command = "HELO OYee\r\n"  
  27.         # 身份确认登录信息  
  28.         connection.send(HELO_command.encode())  
  29.         receive_helo = connection.recv(1024).decode()  
  30.         print(receive_helo)  
  31.         # 250 request finish.  
  32.         if receive_helo[:3]!='250':  
  33.             print("hello errors.\n")  
  34.   
  35.         AUTH_command = "AUTH LOGIN\r\n"  
  36.         # 进行授权登录  
  37.         connection.sendall(AUTH_command.encode())  
  38.         receive_auth_login = connection.recv(1024).decode()  
  39.         print(receive_auth_login)  
  40.         # 334 wait for authetic information.  
  41.         if receive_auth_login[:3]!='334':  
  42.             print("authetic request error.\n")  
  43.   
  44.         connection.sendall((self.username+'\r\n').encode())  
  45.         receive_username = connection.recv(1024).decode()  
  46.         print(receive_username)  
  47.         # 334 wait for username.  
  48.         if receive_username[:3]!='334':  
  49.             print("username error.\n")         
  50.   
  51.         connection.sendall((self.password+'\r\n').encode())  
  52.         receive_password = connection.recv(1024).decode()  
  53.         print(receive_password)  
  54.         # 235 authetic information checked  
  55.         if receive_password[:3]!='235':  
  56.             print("password error.\n")    
  57.           
  58.         connection.sendall(('MAIL FROM: <' + self.fromAddress + '>\r\n').encode())  
  59.         # 邮件的发送地址  
  60.         receive_from = connection.recv(1024).decode()  
  61.         # 250 Mail from confirmed  
  62.         if receive_from[:3]!='250':  
  63.             print("mail from error.\n")  
  64.   
  65.         connection.sendall(('RCPT TO: <' + self.toAddress + '>\r\n').encode())  
  66.         # 邮件的接收地址  
  67.         receive_to = connection.recv(1024).decode()  
  68.         # 250 Mail To confirmed  
  69.         if receive_to[:3]!='250':  
  70.             print("recept to error.\n")  
  71.   
  72.         connection.sendall(("DATA\r\n").encode())  
  73.         # 邮件的正文  
  74.         receive_data = connection.recv(1024).decode()  
  75.         print(receive_data)  
  76.         # 354 data accepting  
  77.         if receive_data[:3]!= '354':  
  78.             print("data transport error.\n")  
  79.   
  80.         contentType = "text/plain"  
  81.         # 内容类型,text/plain表示纯文本类型邮件;还有text/html表示html类型邮件  
  82.   
  83.         message = 'from:' + self.fromAddress + '\r\n'  
  84.         message += 'to:' + self.toAddress + '\r\n'  
  85.         message += 'subject:' + self.subject + '\r\n'  
  86.         message += 'Content-Type:' + contentType + '\t\n'  
  87.         message += '\r\n' + self.msg  
  88.         # 组装邮件的DATA信息  
  89.         connection.sendall(message.encode())  
  90.         # 发送DATA  
  91.         connection.sendall(self.endMsg.encode())  
  92.         # DATA输入结束  
  93.         receive_end = connection.recv(1024).decode()  
  94.         print(receive_end)  
  95.         if '250' != receive_end[:3]:  
  96.             print('data transmission error.')  
  97.         connection.sendall('QUIT\r\n'.encode())  
  98.         # 结束和smtpTCP连接  
  99.         connection.close()  
  100.   
  101. def main():  
  102.     username = input("Username:\n")  
  103.     password = input("Password:\n")  
  104.   
  105.     from_address = input("From:\n")  
  106.     to_address = input("To:\n")  
  107.   
  108.     subject = input("Subject:\n")  
  109.     msg = input("Message:\n")  
  110.     smtp_server = smpt_client(username, password, from_address, to_address, subject, msg)  
  111.     with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:  
  112.         # AF_INET表示使用的是ipv4协议,SOCK_STREAM表示使用的是TCP协议  
  113.         server_socket.connect((HOST,PORT))  
  114.         smtp_server.push(server_socket)  
  115. if __name__ == '__main__':  
  116.     main()  
  • pop3邮件接收客户端

见附件 smtp/ pop3_server.py

  1. import poplib  
  2. 用于和POP进行TCP连接  
  3. from email.parser import Parser  
  4. 解析邮件的字节流  
  5. from email.header import decode_header  
  6. 解析邮件header部分  
  7. from email.utils import parseaddr  
  8. 解析邮件地址部分  
  9.   
  10. # qq服务器的pop服务器域名为pop.qq.com,端口号为110  
  11.   
  12.   
  13. class pop3_server():  
  14.     def __init__(self, pop3_address, email_address, password):  
  15.         self.server = poplib.POP3(pop3_address,110)  
  16.         连接到POP3服务器,qqPOP服务器端口号为110default  
  17.   
  18.         print(self.server.getwelcome())  
  19.         连接成功之后输出欢迎信息  
  20.   
  21.         self.server.user(email_address)  
  22.         self.server.pass_(password)  
  23.         # POP服务器身份认证  
  24.   
  25.         resp,self.mails,octlets = self.server.list()  
  26.         # self.mail中存放着本用户在目标服务器中的邮件信息  
  27.   
  28.     def print_stat(self):  
  29.         获取邮件大小信息  
  30.         print("Message: %s. Size: %s\n" % self.server.stat())  
  31.         邮件的数量和大小  
  32.         return self.server.stat()[0]  
  33.   
  34.     def get_msg(self,index):  
  35.           
  36.         resp,self.lines,octlets = self.server.retr(index)  
  37.         # lines中存储了邮件的原始文本的每一行,可以获取整个邮件的原始文本  
  38.   
  39.         msg_content = b'\r\n'.join(self.lines).decode('utf-8')  
  40.         # b表示后面字符串为二进制字节流内容  
  41.         self.msg = Parser().parsestr(msg_content)  
  42.         经过parsestr处理之后可以得到一个字典  
  43.         # msg为邮件的原始文本  
  44.       
  45.     def decode_str(self,str):  
  46.         解析字符串  
  47.         value, charset = decode_header(str)[0]  
  48.         if charset:  
  49.             value = value.decode(charset)  
  50.         return value  
  51.   
  52.     def get_charset(self):  
  53.         获取编码方式  
  54.         charset = self.msg.get_charset()  
  55.         if charset is None:  
  56.             如果未能获取到messagecharset,则搜索content-type报文头,查找其指定的charset  
  57.             content_type = self.msg.get('Content-Type''').lower()  
  58.             pos = content_type.find('charset=')  
  59.             if pos >= 0:  
  60.                 charset = content_type[pos + 8:].strip()  
  61.         return charset  
  62.   
  63.     def decode_content(self,indent = 0):  
  64.         解码邮件内容  
  65.         if indent == 0:  
  66.             初始页面  
  67.             for header in ['From''To''Subject']:  
  68.                 value = self.msg.get(header, '')  
  69.                 if value:  
  70.                     if header=='Subject':  
  71.                         value = self.decode_str(value)  
  72.                     else:  
  73.                         hdr, addr = parseaddr(value)  
  74.                         name = self.decode_str(hdr)  
  75.                         value = u'%s <%s>' % (name, addr)  
  76.                         # u表示以Unicode的格式进行编码  
  77.                 print('%s%s: %s' % ('  ' * indent, header, value))  
  78.   
  79.         if (self.msg.is_multipart()):  
  80.             # body处理  
  81.               
  82.             parts = self.msg.get_payload()  
  83.             获得message的部分数  
  84.             for n, part in enumerate(parts):  
  85.                 print('%spart %s' % ('  ' * indent, n))  
  86.                 print('%s-----------------------------' % ('  ' * indent))  
  87.                 self.msg = part  
  88.                 self.decode_content(indent + 1)  
  89.                 递归调用,indent用于表示页面递归的次数  
  90.         else:  
  91.             content_type = self.msg.get_content_type()  
  92.             if content_type=='text/plain' or content_type=='text/html':  
  93.                 content = self.msg.get_payload(decode=True)  
  94.                 charset = self.get_charset()  
  95.                 if charset:  
  96.                     content = content.decode(charset)  
  97.                 print('%sText: %s' % ('  ' * indent, content + '...'))  
  98.             else:  
  99.                 print('%sAttachment: %s' % ('  ' * indent, content_type))  
  100.   
  101.     def close_connection(self):  
  102.         关闭和POP服务器的TCP连接  
  103.         self.server.quit()  
  104.   
  105. def main():  
  106.     pop3_address = input("请输入pop3地址:\n")  
  107.     # email_address = pop.qq.com  
  108.     email_address = input("请输入邮件地址:\n")  
  109.     # username = '1149095383@qq.com'  
  110.     password = input("请输入客户密码:\n")  
  111.     # password = 'swgvuxidniltfhcf'  
  112.     server = pop3_server(pop3_address, email_address, password)  
  113.   
  114.     print("邮箱的状态为:\n")  
  115.     server.print_stat()  
  116.   
  117.     while(1):  
  118.         index = input("请输入您要查询的邮件,如果停止查询请输入-1\n")  
  119.         if int(index) < 0 :  
  120.             break  
  121.         server.get_msg(index)  
  122.         server.decode_content()  
  123.   
  124.     server.close_connection()  
  125.     return  
  126.   
  127. if __name__ == '__main__':  
  128.     main()  

3、实验运行结果

(1)发送邮件

①用户输入信息:发送方邮箱地址/发送方邮箱授权码/接收方邮箱地址/邮件主题/邮件内容/是否添加附件

    • 打印调试信息

    • 邮件接受效果

  1. 接收邮件

①用户输入信息登录,可选项(是否显示邮箱状态信息【邮件数量/文件大小】,是否显示邮件列表)

②询问用户对邮件进行操作(若进行操作则打印邮件列表)还是退出程序

③询问用户浏览还是删除邮件:

当选择浏览时:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/549051
推荐阅读
相关标签
  

闽ICP备14008679号