野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 9118|回复: 1

W7500 M0+MAC+TCP/IP 以太网单芯片方案 W7500 W7500EVB用户手册连载(22)--网络篇

[复制链接]
发表于 2018-3-23 19:11:12 | 显示全部楼层 |阅读模式
W7500(W7500P)芯片简介:
W7500 芯片为工业级以太网单芯片解决方案, 集成 ARM Cortex-M0,128KB Flash 及全硬件TCP/IP 核(和W5500、W5100内核一致),特别适用于物联网领域。 使用 W7500EVB, 让您轻松完成原型开发。

全硬件TCP/IP核的最大优点是他在执行联网操作时不需要占用MCU的运行资源,大大增加了MCU的工作效率。

想了解更多关于WIZnet W7500更多信息,或者有技术问题请联系我们:gustin@wisioe.com
深圳炜世科技—WIZnet官方代理商,全程技术支持,价格绝对优势!
电话:0755-86568556
邮箱:support@wisioe.com
回复

使用道具 举报

 楼主| 发表于 2018-3-23 19:12:21 | 显示全部楼层
第二十三章 SMTP23.1 SMTP例程概述
       SMTP例程主要实现通过W7500EVB发送邮件。邮件的发送是依靠SMTP协议,所以使用前,我们先了解下SMTP协议。
23.2 SMTP协议简介
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。通过SMTP协议所指定的服务器,就可以把Email寄到收信人的服务器上了,整个过程只要几分钟。
SMTP服务器则是指遵循SMTP协议的发送邮件服务器,用来发送或中转发出的电子邮件。SMTP使用TCP端口25。要为一个给定的域名指定一个邮件交换服务器,需要使用MX(MaileXchange)DNS。SMTP在整个电子邮件通信中所处的位置如图23.2.1所示。
1.JPG

图23..2.1 SMTP邮件传输过程
23.3 SMTP协议工作原理
SMTP是工作在两种情况下:一是电子邮件从客户机传输到服务器:二是从某一个服务器传输到另一个服务器。SMTP也是个请求/响应协议,命令和响应都是基于ASCⅡ文本,并以CR和LF符结束。响应包括一个表示返回状态的三位数字代码。SMTP在TCP协议25号端口监听连续请求。
smtp连接和发送过程:
(1)建立TCP 连接。
(2)客户端发送HELO命令自己的身份以标识发件人,然后客户端发送MAIL       命令;服务器端以OK作为响应,表明准备接收。
(3)客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行;服务器端可选择是否愿意为收件人接收邮件。
(4)协商结束,发送邮件,用命令DATA发送。
(5)以“.”号表示结束输入内容一起发送出去,结束此次发送,用QUIT命令退出。SMTP服务器基于域名服务DNS中计划收件人的域名来路由电子邮件。SMTP服务器基于DNS中的MX记录来路由电子邮件,MX记录注册了域名和相关的SMTP中继主机,属于该域的电子邮件都应向该主机发送。若SMTP服务器mail.abc.com收到一封信要发到shuer@sh.abc.com,则执行以下过程:
(1)Sendmail 请求DNS给出主机sh.abc.com的CNAME 记录,如有,假若CNAME(别名记录)到shmail.abc.com,则再次请求shmail.abc.com的CNAME记录,直到没有为止。
(2)假定被CNAME到shmail.abc.com,然后sendmail请求@abc.com域的DNS给出shmail.abc.com的MX记录(邮件路由及记录),shmail MX 5shmail.abc.com10 shmail2.abc.com。
(3)Sendmail组合请求DNS给出shmail.abc.com的A记录(主机名(或域名)对应的IP地址记录),即IP地址,若返回值为1.2.3.4(假设值)。
(4)Sendmail与1.2.3.4连接,传送这封给shuser@sh.abc.com的信到1.2.3.4这台服务器的SMTP后台程序。
23.4 SMTP协议命令
       SMTP命令是发送于SMTP主机之间的ASCⅡ信息,可能使用到的命令如下表所示。
命令
描述
DATA
开始信息写作
EXPN<string>
验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELO<domain>
向服务器标识用户身份,返回邮件服务器身份
HELP<command>
查询服务器支持什么命令,返回命令中的信息
MAIL FROM<host>
在主机上初始化一个邮件会话
NOOP
无操作,服务器应响应OK
QUIT
终止邮件会话
RCPT TO<user>
标识单个的邮件接收人;常在MAIL命令后面可有多个rcpt to:
RSET
重置会话,当前传输被取消
SAML FROM<host>
发送邮件到用户终端和邮箱
SEND FROM<host>
发送邮件到用户终端
SOML FROM<host>
发送邮件到用户终端或邮箱
TURN
接收端和发送端交换角色
VRFY<user>
用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
23.5 SMTP协议响应
SMTP响应的一般形式是:XXX Readable Illustration。XXX是3位十进制数;Readable Illustration是可读的解释说明,用来表明命令是否成功等。XXX具有如下的规律:以2开头的表示成功,以4和5开头的表示失败,以3开头的表示未完成(进行中)。常用响应如下所示:
SMTP常用响应
响应代码
描述
Positive Completion Reply
211
System status or help reply(系统状态或系统帮助响应)
214
Help message(帮助信息)
220
Service ready(服务就绪)
221
Service closing transmission channel(服务关闭)
250
Request command completed(要求的邮件操作完成)
251
User not local;the message will be forwarded(用户非本地,将转发)
Positive Intermediate Reply
354
Start mail input(操作失败)
Transient Negative Completion Reply
421
Service not available(服务不可用)
450
Mailbox not available(邮箱不可用)
451
Command aborted: local error(命令未执行:本地错误)
452
Command aborted: insufficient system storage(命令未执行:系统存储不足)
Permanent Negative Completion Reply
500
Syntax error: unrecognized command(命令未识别)
501
Syntax error in parameters or arguments(参数格式错误)
502
Command not implemented(命令不可实现)
503
Bad sequence of commands(错误的命令序列)
504
Command temporarily not implemented(命令参数不可实现)
550
Command is not executed: mailbox unavailable
(命令未执行:邮箱不可用)
551
User not local(用户非本地)
552
Requested action aborted: exceeded storage location
(要求的操作未执行:过量的存储分配)
553
Requested action not taken: mailbox name not allowed
(要求的操作未执行:邮箱名不可用)
554
Transaction failed(操作失败)
       命令和响应的格式是语法,各命令和响应的意思则是语义,各命令和响应在时间上的关系则是同步。
23.6 SMTP例程解析
    由于资源受限,在没有操作系统的支持下,通过单片机发送邮件与传统的电脑操作将有很大的不同。这里用W7500EVB与126邮箱通信为例来具体分析邮件的发送过程。在本示例代码中,发件人邮箱名为:wiznet2013@126.com,邮箱密码为:hello123。收件人邮箱地址为:1846955430@qq.com,邮件内容为:Hello!WIZnet!。如果想用别的邮箱做测试的话,请修改代码中收件人和发件人的邮箱名和密码。
       本文将发送邮件的整个过程分为3个部分讲解,main.c主程序负责初始化和主循环,smtp.c实现邮件的发送及命令信息的处理,dns.c实现SMTP服务器域名的解析,126邮箱的服务器域名为smtp.126.com,我们调用的其他函数在其他文件中声明。
        DNS解析SMTP服务器域名的部分本章前面的章节已经介绍过,这里不再叙述。主函数初始化过程基本一样,主要添加了邮件初始化函数mail_message()。
  1. while(1)
  2.   {
  3.     ret = do_dns(SOCK_DNS, test_buf);   //解析126邮箱服务器IP地址
  4.     if(ret)
  5.     {
  6.       do_smtp();                       //发送邮件
  7.       if(mail_send_ok)
  8.         while(1);
  9.     }
  10.   }
  11. char hello[50]="HELO localhost";               //身份标识命令
  12. char hello_reply[]="250 OK";                   //身份标识成功响应
  13. char AUTH[50]="AUTH LOGIN";                    //认证请求
  14. char AUTH_reply[]="334 dXNlcm5hbWU6";          //认证请求发送成功响应
  15. char name_126[100]="wiznet2013@126.com";       //126登录邮箱名
  16. char base64name_126[200];                      //126登录邮箱名的base64编码
复制代码
用户在使用SMTP例程时,可根据代码注释与自身需求更改以下字符串内容即可。
  1. char name_reply[]="334 UGFzc3dvcmQ6";                //发送登录名成功响应
  2. char password_126[50]="hello123";                    //126 登陆邮箱密码
  3. char base64password_126[100];                        //base64 126登录邮箱密码
  4. char password_reply[]="235 Authentication successful";//登陆成功响应
  5. char from[]="wiznet2013@126.com";                    //发人邮箱
  6. char from_reply[]="250 Mail OK";
  7. char to[]="2429075983@qq.com";                       //收件人邮箱
  8. char to_reply[]="250 Mail OK";
  9. char data_init[10]="data";                           //请求数据传输
  10. char data_reply[]="354";                             //请求成功响应 HEAD
  11. char Cc[]="2429075983@qq.com";                       //抄送人邮箱
  12. char subject[]="Hello!WIZnet!";                      //主题
  13. char content[]="Hello!WIZnet!";                      //正文
  14. char mime_reply[]="250 Mail OK queued as";           //邮件发送成功响应
  15. char mailfrom[50]="MAIL FROM:<>";
  16. char rcptto[50]="rcpt to:<>";
  17. char mime[200]="From:\r\n";
  18. char mime1[50]="To:\r\n";
  19. char mime2[50]="Cc:\r\n";
  20. char mime3[50]="Subject:\r\n";
  21. char mime4[50]="MIME-Version:1.0\r\nContent-Type:text/plain\r\n\r\n";
  22. char mime5[50]="\r\n.\r\n";
复制代码

       邮件发送具体过程很简单,先解析126邮箱的服务器域名smtp.126.com,成功以后就执行邮件发送函数,邮件发送成功以后就跳出循环。主循环处判断到邮件发送成功后让程序进入了一个死循环,这样程序将不再跳到邮件发送函数去,避免重复发送相同的邮件,这样使得在W7500EVB的运行模式下,按一下Reset键或者上电一次,只发送一封邮件。下面介绍发送邮件主函数:
  1. 1.      void do_smtp(void)                     //SMTP 主函数
  2. 2.      {
  3. 3.        uint8_t ret;
  4. 4.        uint8_t ch=SOCK_SMTP;               //定义一个变量并赋值SMTP通信socket号
  5. 5.        uint16_t len;
  6. 6.        uint16_t anyport=5000;             //定义SMTP Client的通信端口号
  7. 7.        uint8_t Smtp_PORT=25;              //SMTP Server 的端口号,默认为25
  8. 8.        memset(RX_BUF,0,sizeof(RX_BUF));
  9. 9.        switch(getSn_SR(ch))              //读取W7500的socket状态
  10. 10.     {
  11. 11.       case SOCK_INIT:                   //初始化完成
  12. 12.                connect(ch, ConfigMsg.rip ,Smtp_PORT );//连接SMTP Server
  13. 13.            break;
  14. 14.            case SOCK_ESTABLISHED:       //socket建立成功
  15. 15.           if(getSn_IR(ch) & Sn_IR_CON)
  16. 16.           {
  17. 17.             setSn_IR(ch, Sn_IR_CON);    //清除接收中断标志
  18. 18.           }
  19. 19.           if ((len = getSn_RX_RSR(ch)) > 0)            
  20. 20.           {
  21. 21.             while(!mail_send_ok)        //如果邮件没有发送成功
  22. 22.             {
  23. 23.               memset(RX_BUF,0,sizeof(RX_BUF));//接受缓存的内存空间清零
  24. 24.               len = recv(ch, (uint8_t*)RX_BUF,len); //W7500接收数据并存入RX_BUF
  25. 25.               send_mail();              //发送邮件
  26. 26.             }
  27. 27.             disconnect(ch);             //断开socket连接
  28. 28.           }
  29. 29.       break;
  30. 30.       case SOCK_CLOSE_WAIT:   /*等待socket关闭*/  
  31. 31.         if ((len = getSn_RX_RSR(ch)) > 0)
  32. 32.         {      
  33. 33.           while(!mail_send_ok)
  34. 34.           {
  35. 35.             len = recv(ch, (uint8_t*)RX_BUF, len);
  36. 36.             send_mail();
  37. 37.           }
  38. 38.         }
  39. 39.         close(ch);
  40. 40.         break;
  41. 41.       case SOCK_CLOSED: //socket关闭
  42. 42.           if((ret=socket(ch, Sn_MR_TCP, anyport, 0x01)) != ch)//开启socket
  43. 43.           { //移植过程中,W7500旧版本库flag标志位用0x01和0X00没有区太大影响,但是用iolibary库时,一定改为非零的,一般为0X01。
  44. 44.             //一定注意这个问题,也是之前不能调试成功的问题所在
  45. 45.             printf(" %d\r\n",ret);                                    
  46. 46.           }
  47. 47.         break;
  48. 48.       default:
  49. 49.       break;
  50. 50.     }
  51. 51.   }
复制代码

      由于SMTP发送邮件使用TCP协议,是面向链接的可靠传输。我们这里还是使用熟悉的TCP状态机来实现数据交互。第4行定义W7500的一个socket用于SMTP通信,第5行定义一个变量用于存储W7500接收到的数据长度,第6、7行就是分别定义client、server端的端口号,这里需要注意的是SMTP服务器默认监听的TCP端口是25。因此第6行里面的W7500本地端口可以随便设置。第8行把用于保存W7500接收数据缓存的RX_BUF清空。  
       通过第9行获取socket 状态,然后根据在后面的switch中针对socket的不同状态做不同的操作。第11行,当时socket处于初始完成状态时,向SMTP服务器发送链接请求。第14行,当socket处于连接建立状态时,清空Sn_IR响应的中断位。如果邮件没有发送成功,就执行接收SMTP Server的响应,发送邮件,直到邮件发送OK,然后断开连接。第30行,当socket处于等待关闭状态时,由于socket此时还能进行数据交互,所以执行动作就和socket连接建立状态相同。第42行,当处于socket关闭状态时,初始化socket,并将其配置为TCP 模式。下面介绍邮件具体的发送过程。
  1. 1.      uint8_t SMTP_STATE=waitfor220;
  2. 2.      void send_mail(void)           
  3. 3.      {
  4. 4.        switch(SMTP_STATE)
  5. 5.        {
  6. 6.          case waitfor220:                         //等待连接成功的正确响应状态
  7. 7.            if(strstr((const char *)RX_BUF,"220")!=NULL)
  8. 8.            {
  9. 9.                send(SOCK_SMTP,(uint8_t *)hello,strlen(hello));//发送hello命令
  10. 10.             SMTP_STATE=waitforHELO250;  
  11. 11.         }
  12. 12.       break;
  13. 13.       case waitforHELO250:                 //等待hello命令的正确响应状态
  14. 14.         if(strstr((const char *)RX_BUF,hello_reply)!=NULL&&strstr((const char *)RX_BUF,"Mail")==NULL)
  15. 15.         {
  16. 16.             send(SOCK_SMTP,(uint8_t *)AUTH,strlen(AUTH));//发送AUTH认证请求
  17. 17.             SMTP_STATE=waitforAUTH334;
  18. 18.         }
  19. 19.       break;
  20. 20.       case waitforAUTH334:                 //等待AUTH认证请求的正确响应
  21. 21.         if(strstr((const char *)RX_BUF,AUTH_reply)!=NULL)
  22. 22.         {
  23. 23.             send(SOCK_SMTP,(uint8_t *)base64name_126,strlen(base64name_126));//发送邮箱登录名
  24. 24.             SMTP_STATE=waitforuser334;
  25. 25.         }
  26. 26.       break;
  27. 27.       case waitforuser334:                   //等待邮箱登录名登陆成功的正确响应
  28. 28.         if(strstr((const char *)RX_BUF,name_reply)!=NULL)
  29. 29.         {
  30. 30.             send(SOCK_SMTP,(uint8_t *)base64password_126,strlen(base64password_126));//发送邮箱登录密码
  31. 31.             SMTP_STATE=waitforpassword235;
  32. 32.         }
  33. 33.       break;
  34. 34.       case waitforpassword235:            //等待邮箱密码登陆成功正确响应
  35. 35.         if(strstr((const char *)RX_BUF,password_reply)!=NULL)
  36. 36.         {
  37. 37.             send(SOCK_SMTP,(uint8_t *)mailfrom,strlen(mailfrom));//发送发件人信息
  38. 38.             SMTP_STATE=waitforsend250;
  39. 39.         }
  40. 40.       break;
  41. 41.       case waitforsend250:                   //等待发件人信息发送成功正确响应
  42. 42.         if(strstr((const char *)RX_BUF,from_reply)!=NULL&&strstr((const char *)RX_BUF,"queued as")==NULL)
  43. 43.         {
  44. 44.             send(SOCK_SMTP,(uint8_t *)rcptto,strlen(rcptto)); //发送收件人信息
  45. 45.             SMTP_STATE=waitforrcpt250;
  46. 46.         }
  47. 47.       break;
  48. 48.       case waitforrcpt250:                    //等待收件人信息发送成功正确响应
  49. 49.         if(strstr((const char *)RX_BUF,to_reply)!=NULL)
  50. 50.         {
  51. 51.             send(SOCK_SMTP,(uint8_t *)data_init,strlen(data_init));//发送数据传输请求命令
  52. 52.             SMTP_STATE=waitfordate354;
  53. 53.         }
  54. 54.       break;
  55. 55.       case waitfordate354:                   //等待发送数据传输请求命令发送成功正确响应
  56. 56.         if(strstr((const char *)RX_BUF,data_reply)!=NULL)
  57. 57.         {
  58. 58.             send(SOCK_SMTP,(uint8_t *)mime,strlen(mime));  //发送mime类型命令
  59. 59.             SMTP_STATE=waitformime250;
  60. 60.         }
  61. 61.       break;
  62. 62.       case waitformime250:          //等待mime的正确响应
  63. 63.         if(strstr((const char *)RX_BUF,mime_reply)!=NULL)
  64. 64.         {
  65. 65.             mail_send_ok=1;          //邮件发送成功
  66. 66.             printf("mail send OK\r\n");   
  67. 67.         }
  68. 68.       break;
  69. 69.       default:
  70. 70.       break;
  71. 71.     }
  72. 72.   }
复制代码

      接下来是发送邮件的具体过程,同样是个状态机。根据SMTP 服务器的响应,我们一共定义了9个不同的状态。
       第6行,TCP连接建立后,等待SMTP 服务器的220响应,如果接收到服务器成功回应,发送HELO localhost的命令,进入等待250OK响应的状态。
       第13行,如果SMTP 服务器正确回应,发送AUTH 认证命令,等待认证请求的正确响应。
       第20行,当AUTH认证请求正确回应时,发送BASE64编码的登录名,进入等待SMTP 服务器正确响应。
       第27行,当服务器成功回应时,发送经过BASE64编码的登录密码。
       第34行,password235成功响应,发送发件人邮箱名。
       第41行,如果得到250 Mail OK的响应,发送收件人邮箱。
       第48行,如果收到250 Mail OK 的响应,请求发送数据,发送DATA 命令,进入等待354报头格式的响应。
       第55行,发送mime类型的邮件正文,然后等待服务器的回应。
       第62行,如果SMTP Server成功回应,邮件发送成功,此时把邮件发送成功的标志位置1。
程序编译无误以后烧录,打开串口工具,首先解析到smtp.126.com邮件服务器的IP,然后发送邮件,如果发送成功,会得到mail send OK的信息,结果如图23.6.1

图23.6.1 SMTP例程打印与邮件接收截图

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-11-26 02:22 , Processed in 0.086087 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表