程序员,你会问问题吗?

由于一直从事技术和平台产品方面的工作,我们部门经常会收到公司内外同事和同仁的问题邮件,有些好的问题能让你发现自己技术上的缺陷、产品的bug或提升的空间,去思考、回答和解决这样的问题真是一件让人愉悦,充满挑战和成就感的事情。但是非常遗憾的是,这样的好问题却是凤毛麟角。我经常会被一些莫名其妙的问题搞的啼笑皆非,比如:

  1. 程序运行过程中突然内存溢出,该如何解决?
  2. 如何配置JVM的虚拟机参数?
  3. 程序部署到Linux上后,页面出现中文乱码,是不是中间件的配置出现问题了?
  4. 集群节点不能自动复制,如何解决?

 

最可气是第四个问题,经过了解环境逐一排查,最后发现两个节点根本就ping不通嘛,这种“异常”在现场该是多么容易发现啊!

当然,类似的傻问题我年轻的时候也问过,谁会不犯错呢,真正让我认识到这一点的重要性,还是在工作中与国外程序员的邮件交流。在2005年期间,与国外程序员共同维护公司内部的一个平台级产品,邮件往来必不可少,慢慢的我发现国外的程序员提的问题或报的bug都非常有规律,每个问题或bug都有非常清晰的标题,正文是环境描述,已经采取了什么措施、结果,相关日志,Core dump,图片等等,一般读完邮件就能非常清楚的了解对方想要表达的意图和希望你能提供的帮助,而且你也知道该做什么,如何回复等等。久而久之,自己也不好意思再去写那些傻问题了。

那么作为技术人员,如何去问一个让双方都满意的好问题并最大程度的得到回复呢?这一点对提问者重要,对被问者同样重要,大好人生,谁也不愿意为一个烂问题浪费时间。

简单总结一下,如果你按照以下步骤进行,提出的问题一定会更靠谱一些,提出好的问题是你提升的第一步,其实这个过程在提问之前已经开始了:

  1. 遇到问题不要急着问别人,在时间允许的情况下看是否自己能够解决,一方面锻炼自己分析问题和解决问题的能力,另一方面,一旦问题解决了,问题就不是问题,而是你的经验和知识库。况且现在互联网有那么多的技术资料和各类问答网站,想碰到一个别人没碰到的问题,已经非常困难了,除非是内部产品。
  2. 如果做了努力依然不能解决,或者客观条件不允许你自己解决了,那么首先要选择提问对象,不管是社区还是公司同事,确保他是你所知道的最佳解决人选。
  3. 你需要一个好的标题,用清晰的短句描述你遇到的问题
  4. 至关重要的正文

    (1)用清晰的语言描述你遇到的问题
    (2)提供软件环境,包括操作系统、数据库等相关软件及其版本号
    (3)问题是否可以重现,采用什么方式重现
    (4)采用了什么措施解决问题,结果(可提供日志、程序、截图等描述)
    (5)尽可能提供问题相关的可分析文件,包括日志、截图和Core dump等
    (6)不要长篇大论,简明扼要,描述主要问题

  5. 最后,不要忘了说请和谢谢,毕竟你需要别人帮助你解决问题,没人欠你什么。  

本文链接

博客园_首页

关于《Java封面》一些感想

今天正好看了博客园的首页的一篇文章 《Java封面》 ,初次一看,觉得这个貌似很这个说的完全都不靠谱啊,再一看,原来是2001年写的,然后在看看作者,发现原来是 硅谷创业教父  Paul Graham写的,联想到之前看到他写的一本书《黑客与画家》,突然能够明白为什么他这么认为java前途了。

java语言是在 1995年发明的,当初只是为了做一些机顶盒相关的应用,设计的也比较简单。那个时候java并不出名,java之所以比较火爆,就是因为互联网的发展,是由于当初的在浏览器里面能够运行java编写的小程序applet,你想想,那个时候互联网才刚开始发展,能够在网页上面运行动画,是多么牛逼的东西,由于当初java非常火爆,导致javascript 这门语言,想和java搭上关系,其实完全和java一点关系都没有,当初我学习javascript的时候,我一直好奇的是,这门语言完全和java没有关系,干嘛启用这么一个名字。

由于互联网的快速发展,微软出了asp的网页编程语言,记得当初我走B/S道路的时候,就是因为学习了asp这一门语言,发现能够做出东西来产品,比当初学习c和c++只能够写出hello word 这样的编程语言相比,它有趣多了。这个时候java也出了相关的网页编程语言jsp,然后慢慢的推出java三个平台,j2me,j2ee,j2se,分别定位移动平台,企业平台,客户端平台。

我初步觉得 Paul Graham 之所以当初对java有如此的认识,其实和当时所处的时代有关。当初的互联网刚刚起步,社会对互联网的认识才刚刚开始,当时的网速也不快,大多数复杂的企业应用都是采用的C/S模式,还没有慢慢步入B/S模式,而目前java主要运用都是在企业软件里面,现在大多数企业应用都是建立在B/S模式上的,所以说还是互联网成就了java。

现在来看看,作者当时的很多想法,应该是错误,

比如说着说:真正的标准是不是需要推广,相反,现在的标准之争原来越频繁,很多IT大佬在左右的IT编程语言的发展。一个标准推出的好处就是减少重复和浪费,就像操作系统,如果只有一个标准,大家都按照这个标准做,就不会出现为每个厂商就做一个操作系统软件,造成极大的浪费,幸好互联网定义了http标准协议,否则我难以想想互联网是什么样的。

java的目标太低:我看了黑客和画家,作者是一个典型的geek主义者,认为程序员是一个对技术要求很高的职业,要有喜欢挑战的精神,是一个典型的个人主义者。而java太简单。但是相反,随着IT技术的发展,对编程的要求不在那么高,只需要受过一个普通教育的人也能够开发出来很好的应用的人,编程只是一个实现功能的方式,要求越简单越好,越简单生产效率越高,而简单的东西才能流传的更久和更广。

Java 动机不纯:这个我觉得真的没有什么好说的,对付微软很正常,如果有市场就行,微软还不是最后出了.net来对付java。开始java只是为了解决机顶盒问题的,后来发现java的这种设计思想非常不错,可以解决很多问题,最大的好处就是跨平台。慢慢的就往更多的领域发展。

Java 有太多维护人员:java被一个委员会所维护,其实也不叫维护吧,这个委员会(JCP)只是规划java的未来的发展方向以及给java本身添加一个功能,我觉得java最大的特点就是 定义了一个功能,但是不管这个功能是如何实现的,只要满足功能的规约。所以我觉得学习java最大的一个特点就是规范,你会发现一大堆规范,最常见的就是java虚拟机规范,规范主要是讲解要做什么,限制是什么,产生什么结果等等,并不关心怎么实现。

官僚作风太重: 这个比较有意思,仁者见仁,智者见智吧。java的确会有一大堆规范,比较不适合geek,但是比较适合软件这个行业。如果有一大堆规范,那么每个人都会按照这个规范去做,每个人写出来的程序差不多,对于大型IT企业是很喜欢的,毕竟替代一个程序员的代价很低,大家的思想都差不多。

后面的几点我觉得都有点牵强,但是从作者的观点也可以看出来 对于码农的认知是不同。至少看完了《黑客与画家》,我发现作者是有强烈的黑客情节的(hack the problem),作者对于程序员的认识和现在对于程序员的认知应该是不同的。其实这个也是我最近在思考的一个问题:程序员的价值是什么。随着软件开发行业越来越公开化,开源世界的影响越来越大,程序资源也越来越多,很多功能的实现都有特定的开源帮助我们来实现,我们可以随时拿来用,以前我们的思维是如何实现这个功能,那么现在变成有没有类似的解决方案可以拿来用,程序员原来越偏向一个程序组装者或者解决方案者,那么程序员新的价值在那里 ?

 

本文链接

博客园_首页

[C# 网络编程系列]专题十:实现简单的邮件收发器

引言:

在我们的平常工作中,邮件的发送和接收应该是我们经常要使用到的功能的。因此知道电子邮件的应用程序的原理也是非常有必要的,在这一个专题中将介绍电子邮件应用程序的原理、电子邮件应用程序中涉及的协议和实现一个简答的电子邮件收发器程序。

 

一、邮件应用程序基本知识

1.1 电子邮件原理及相关协议

  说到电子邮件的原理,其实和我们现实生活中寄邮件和寄包裹是一样的原理的。就让我们先回顾下现实生活中寄邮件的流程吧——首先,我们先写好信,信封上面写好收信人的地址,写信人的地址,然后把信放到寄信箱中,然后邮局的人会某个时候去这个信箱中的信取出来,然后邮局的人根据信封上写的收信人地址进行转发到当地的邮局,当地邮局然后把信寄到收信人的信箱中(寄包裹的话可能会电话联系,像我们在淘宝,京东买的东西的,收货人就是通过电话联系一样),最后收信人会到自己的信箱中收取信件。上面大致是我们平时生活中寄信的一个流程的。前面已经讲过电子邮件的原理和这个差不多的,下面就介绍了本专题中电子邮件的原理,大家可以和现实生活中的寄信过程进行对比下的,这样可以更加容易理解和掌握:

  我们通过电子邮件应用(例如 基于客户端的Outlook电子邮件软件 和一些基于Web的电子邮件系统——新浪邮箱、谷歌邮箱、QQ邮箱等都属于电子邮件应用)将一封写好的邮件(相当于现实生活中的信,当然邮件也要写明收件人地址,邮件内容等信息的)通过电子邮件协议(SMTP,在后面的电子邮件相关协议中会介绍)发送到SMTP服务器(就是存储邮件的地方,相当于生活中的邮局一样),然后SMTP服务器根据收件人的地址通过SMTP协议转发到相应SMTP接收服务器上,(SMTP服务器进行转发相当于现实生活中邮局的人配送信的过程,配送到收件人当地的邮局,然而现实生活中邮局都是一家,所以可以相互识别——意思就是发送到当地邮局,当地邮局会接收,并且帮助你发送到指定人的信箱中,在网上上就是通过SMTP协议来规定这样的一个过程的,发送到别人的SMTP服务器上别人的服务器必须要认识发送来的邮件并接收)结束,接收端邮件服务器(POP3服务器)把邮件存放到接受者的电子信箱内(相当于当地邮局的人把信放到收信人的邮箱中),最后收件人可以登录自己的电子信箱,再与POP3服务器进行连接,从POP3服务器上下载发送来的邮件,这样在收件人的电子信箱中就可以看到发送来的电子邮件了(这就是现实生活中收信人从自己的信箱中取信的一个过程)。

  注:括号中都是个人的理解,如果有什么不对的地方还望大家指出来,我好及时更正。

 上面已经把电子邮件的原理和现实生活中寄信的过程进行对比,相信大家可以更加清楚电子邮件的原理和发送接收过程的,其实网络上的很多应用都可以以现实生活的例子去理解,这样的话我认为可以加深对知识的理解。下面就介绍下电子邮件中的相关协议的内容:

  网络上的应用的核心就是协议,因为协议让网络上的客户端相互认识发生来的数据,所以电子邮件应用也不例外,也有相关的电子邮件协议来完成发送电子邮件和接收电子邮件的过程,这些协议主要是:SMTP(简单邮件传输协议,Simple Mail Transfer Protocol)、POP3(邮局协议,Post Office Protocol)和IMAP(网络邮件访问协议,Internet Message Access Protocol)。

  •  SMTP——SMTP 主要负责将邮件从一台机器转发至另一台机器(可以对照上面电子邮件的过程来理解SMTP的作用)
  •  POP3——3表示POP协议的版本,主要负责将邮件从邮箱中(POP3服务器)传输到本地计算机。
  •  IMAP——现在常用的版本为第四版本,即IMAP4,主要负责邮件的检索和处理功能,客户端不需要下载邮件到本地计算机,可直接从邮件客户端软件对服务器上的信件和文                      件目录进行操作,它是POP3的替代协议的。

1.2 邮件系统的分类

  邮件系统主要分为两类的——基于客户端的邮件系统和基于Web浏览器的邮件系统。Office OutLook就是基于客户端的邮件客户端系统,而像我们经常使用的QQ邮箱、新浪、网易邮箱等都是属于基于Web浏览器的邮件系统,基于客户端的邮件系统的收发过程,通过下面的图片来描述(图片从网上摘下的):

  图 1.1 基于客户端的邮件收发过程

 

发送方通过邮件客户端,将编辑好的邮件向邮件服务(SMTP服务器,在发送过程中也叫发送端邮件服务器)发送,发送端邮件服务器根据收件人的地址来识别接收端邮件服务器(POP3服务器),然后向POP3服务器发送邮件信息,接收端邮件服务器将邮件存放在接收者的电子信箱中,并告知接收者有新邮件,接收者通过邮件客户端与POP3服务器连接后,就可以查看新邮件。

  然而,基于Web浏览器的邮件系统与基于客户端的邮件系统不同的地方有:

  • 基于Web浏览器邮件系统用户代理(代理的概念也就是用户不是直接与服务器进行通信,而是通过代理的方式,让代理去与服务器通信,然后用户在从代理中获的服务器的信息,代理也就是中间人的作用,相当于生活中中介,在.net中很多技术都用到了代理,例如委托的概念其实也就是代理的一个概念的)是Web浏览器,基于客户端的邮件系统而是邮件客户端应用程序,一般是Windows Form程序。
  • 浏览器发送邮件到SMTP服务器和从POP3服务器中获得邮件的方式都是通过HTTP协议来实现,与基于客户端的邮件系统不同(基于客户端的邮件系统发送通过SMTP协议或ESMTP(Extended SMTP),获得通过POP3或IMAP协议)。

1.3 目前主要的电子邮件服务系统

  电子邮件服务系统——就是向大家提供邮箱服务的服务系统,这样的系统当然是由专门的公司进行研发的,我们一般叫这样的公司为邮件服务商,我们平常使用的网易邮箱,新、Gmail邮箱等都是建立在电子邮件服务系统(这里我的理解是——我们使用的新浪,网易等邮箱相当于现实生活中每个人的信箱,通过信箱可以获得邮局来的信,同样道理通过邮箱可以获得邮件服务系统的邮件,这样电子邮件系统相当于邮局) 。现在主要电子邮件服务系统主要有下面几种:

  • 基于Postfix/Qmail的邮件系统。例如,雅虎邮箱基于Qmail系统
  • 微软Exchange 邮件系统
  • IBM Lotus Domino邮件系统
  • Scalix邮件系统
  • Zimbra邮件系统
  • MDeamon邮件系统

 

二、.Net 平台对邮件发送功能的支持

   在.NET类库中,在System.Net.Mail命名空间下定义了对邮件处理的类,这样使邮件的发送更加方便(这些类也就是对SMTP协议的封装,使我们更好地区编程,只需要使用类中的方法和属性等去完成邮件的发送,避免写复杂的SMTP协议的命令),下面是一张在System.Net.Mail命名空间下对邮件发送的支持的类截图:

  从图片中类的名字中也可以看出每个类的作用的,在这里我就不一个介绍的, 大家可以参考MSDN去看每个类的使用,并且我在后面程序的实现部分也会有详细的注释去介绍程序中使用到类的使用。从图中还可以i看出一点——就是只有SMTP的字样,却没有POP3这样的字样的,这说明.Net类库本身中并没有提供对POP3协议的封装类,但是我们可以使用Jmail组件来完成从POP3服务器中收取邮件的功能,具体的使用将在后面的邮件收发器程序中邮件的接收部分介绍的。

 

三、邮件收发器程序的实现

 3.1 邮件发送功能的实现

  3.1.1 SMTP协议

  SMTP 协议是用于电子邮件的传输的协议,电子邮件是通过SMTP服务器进行发送的,SMTP服务器的默认端口为25,通常发送邮件有两种方式——一种是不使用客户端认证,即客户端可以使用匿名发送邮件(这种方式叫做SMTP);另一种是客户端必须提供用户名和密码认证(这种方式叫做ESMTP,Extended SMTP)目前大部分邮件服务器采用用户名和密码认证的方式。

  客户端发送邮件过程为——先通过客户端软件(本程序中的邮件收发器)将邮件发送到SMTP服务器,然后再由SMTP服务器发送到目标SMTP服务器。下面介绍SMTP协议的内容:

  SMTP协议总共定义了14个命令,命令由命令码和气候的参数域组成, 不区别大小写的(通过前面专题的讲述可以得出各个协议的命令组成都差不多的),下面就简单介绍下5个常用的命令码

名称  

解释

HELO或EHLO

发送连接到服务器的命令,EHLO主要用于与ESMTP服务器建立连接时发送的命令

MAIL FROM

指定发件人的邮件地址

Rcpt to

指定收件人的邮件地址

Data

指定邮件正文内容,邮件内容以单独一行   ”.” 表示接触

Quit

关闭与服务器的连接,然后退出

  电子邮件由信封、首部、正文和结束符号4部分组成,下面就具体介绍下这4个部分的内容:

  1. 信封

  信封包括发信人的邮件地址和接收人的邮件地址,具体对应两条SMTP命令——Mail from: mytest1989@sina.cn(发信人的地址)和Rcpt to:  794170314@qq.com

  2. 首部

  首部中常用的命令有:

  •   Subject:<邮件主题>——表示邮件的主题
  •   Date:<时间>——表示发邮件的时间
  •   reply-to:<邮件地址>——表示邮件的回复地址
  •   Content-Type:<邮件类型>——表示邮件包含文本、HTML超文本和附件的类型。
  •       X-Priority:<邮件优先级>——表示邮件发送的优先级,优先级为3表示为普通邮件;如 X-Priority:3

  3. 正文

  正文当然指的就是邮件的内容了, 用Data命令指定,首部以一个空行结束,下面就是正文部分

  4. 结束符号

  邮件以“.”结束,

  接收方收到SMTP命令之后,会给出一个响应码,每个命令都只有一个响应码,SMTP响应码也是由3位数字组成,后面附加一些文本信息,响应信息的格式为:

     响应码<空格>文本信息<回车换行>

  客户端发出一条命令后,服务器端返回一个响应,发送者在发送下一条命令前必须等待服务器的响应,成功接收到响应码后才继续发送命令。

    附:SMTP常用的响应码:

响应码

解释

响应码

解释

211

系统状态或系统帮助响应

421

服务未就绪,关闭了传输通道

214

帮助信息

501

参数格式错误

220

服务就绪

502

命令不可实现

221

服务关闭传输通道

535

用户验证失败

235

用户验证成功

553

邮箱名不可用,要求的操作未执行

334

等待用户输入验证

554

操作失败

354

开始邮件输入

 

 

 

3.1.2 邮件的发送过程

  第一步:客户端与服务器建立连接(该步中客户端首先发送EHLO local 连接命令,服务器如果返回“220”响应码表示服务器准备就绪了,客户端再继续发送“Auto login”命令,请求登录,服务器收到命令后返回“334”响应码,表示要输入用户名,之后客户端发送用户名命令,等到响应后再发送密码命令,具体在程序的实现中也会有注释。)

  第二步:客户端发送邮件的信封

  第三步:开始发送邮件数据,(包括邮件首部,正文和结束符号,注:结束符号要单独占一行,表示邮件发送结束)

  第四步: 客户端与服务器断开连接。

 

3.1.3 发送功能的实现代码

相信有了上面的理论解释邮件发送的过程后,实现邮件发送的功能并不难的,并且.net类库中SMTPClient类帮我们封装了SMTP协议,使得我们实现邮件发送功能就不要记住那些具体的命令了, 只需要使用该类中提供的方法来完成邮件的发送(当然你也可以通过发送命令的方式实现,SMTPClient类的方法也是帮我们完成发送命令功能而已的),下面是邮件发送功能的核心代码:

1 #region 邮件发送功能代码
2 // 添加附件
3 private void btnAddFile_Click(object sender, EventArgs e)
4 {
5 OpenFileDialog openFileDialog = new OpenFileDialog();
6 openFileDialog.CheckFileExists = true;
7 // 只接受有效的文件名
8 openFileDialog.ValidateNames = true;
9 // 允许一次选择多个文件作为附件
10 openFileDialog.Multiselect = true;
11 openFileDialog.Filter = 所有文件(*.*)|*.*;
12 if (openFileDialog.ShowDialog() != DialogResult.OK)
13 {
14 return;
15 }
16 if (openFileDialog.FileNames.Length > 0)
17 {
18 // 因为这里允许选择多个文件,所以这里用AddRange而没有用Add方法
19 cmbAttachment.Items.AddRange(openFileDialog.FileNames);
20 }
21 }
22
23 // 删除附件
24 private void btnDeleteFile_Click(object sender, EventArgs e)
25 {
26 int index = cmbAttachment.SelectedIndex;
27 if (index == -1)
28 {
29 MessageBox.Show(请选择要删除的附件!, 提示, MessageBoxButtons.OK, MessageBoxIcon.Information);
30 return;
31 }
32 else
33 {
34 cmbAttachment.Items.RemoveAt(index);
35 }
36 }
37
38 // 发送邮件
39 private void btnSend_Click(object sender, EventArgs e)
40 {
41 this.Cursor = Cursors.WaitCursor;
42 // 实例化一个发送的邮件
43 // 相当于与现实生活中先写信,程序中把信(邮件)抽象为邮件类了
44 MailMessage mailMessage = new MailMessage();
45 // 指明邮件发送的地址,主题,内容等信息
46 // 发信人的地址为登录收发器的地址,这个收发器相当于我们平时Web版的邮箱或者是OutLook中配置的邮箱
47 mailMessage.From = new MailAddress(tbxUserMail.Text);
48 mailMessage.To.Add(txbSendTo.Text);
49 mailMessage.Subject = txbSubject.Text;
50 mailMessage.SubjectEncoding = Encoding.Default;
51 mailMessage.Body = richtbxBody.Text;
52 mailMessage.BodyEncoding = Encoding.Default;
53 // 设置邮件正文不是Html格式的内容
54 mailMessage.IsBodyHtml = false;
55 // 设置邮件的优先级为普通优先级
56 mailMessage.Priority = MailPriority.Normal;
57 //mailMessage.ReplyTo = new MailAddress(tbxUserMail.Text);
58
59 // 封装发送的附件
60 System.Net.Mail.Attachment attachment = null;
61 if (cmbAttachment.Items.Count > 0)
62 {
63 for (int i = 0; i < cmbAttachment.Items.Count; i++)
64 {
65 string fileNamePath = cmbAttachment.Items[i].ToString();
66 string extName = Path.GetExtension(fileNamePath).ToLower();
67 if (extName == .rar || extName == .zip)
68 {
69 attachment = new System.Net.Mail.Attachment(fileNamePath, MediaTypeNames.Application.Zip);
70 }
71 else
72 {
73 attachment = new System.Net.Mail.Attachment(fileNamePath,MediaTypeNames.Application.Octet);
74 }
75
76 // 表示MIMEContent-Disposition标头信息
77 // 对于ContentDisposition具体类的解释大家可以参考MSDN
78 // 这里我就不重复贴出来了,给个地址: http://msdn.microsoft.com/zh-cn/library/System.Net.Mime.ContentDisposition.aspx (着重看备注部分)
79 ContentDisposition cd = attachment.ContentDisposition;
80 cd.CreationDate = File.GetCreationTime(fileNamePath);

81 cd.ModificationDate = File.GetLastWriteTime(fileNamePath);
82 cd.ReadDate = File.GetLastAccessTime(fileNamePath);
83 // 把附件对象加入到邮件附件集合中
84 mailMessage.Attachments.Add(attachment);
85 }
86 }
87
88 // 发送写好的邮件
89 try
90 {
91 // SmtpClient类用于将邮件发送到SMTP服务器
92 // 该类封装了SMTP协议的实现,
93 // 通过该类可以简化发送邮件的过程,只需要调用该类的Send方法就可以发送邮件到SMTP服务器了。
94 smtpClient.Send(mailMessage);
95 MessageBox.Show(邮件发送成功!, 提示, MessageBoxButtons.OK, MessageBoxIcon.Information);
96
97 }
98 catch(SmtpException smtpError)
99 {
100 MessageBox.Show(邮件发送失败:[" + smtpError.StatusCode + "];["
101 + smtpError.Message+"];\r\n["+smtpError.StackTrace+"].
102 ,错误,MessageBoxButtons.RetryCancel,MessageBoxIcon.Error);
103 }
104 finally
105 {
106 mailMessage.Dispose();
107 this.Cursor = Cursors.Default;
108 }
109 }
110
111 #endregion

 

3.2 邮件接收功能的实现

3.2.1 POP3协议

   前面介绍了邮件的发送,当然接收者需要登录邮箱来查看收到的邮件了,此时就必有有一个协议去读取服务器上邮件,POP3就是这样的一个协议。还有两外一种协议也是用来接收邮件的——IMAP协议,它与POP3协议区别有:1. IMAP使用的端口号是143而POP3邮件服务器通过监听110端口来提供POP3服务;

                       2 . IMAP 允许客户端在邮件服务器上建立文件夹来保持邮件,而不用把邮件下载到本地。而POP3需要把邮件下载到本地。

  和SMTP协议一样,客户端要通过POP3协议从POP3服务器上获取邮件,也需要先与POP3服务器建立TCP连接,等待服务器向客户端发送确认信息表明连接成功时,客户端才可以继续发送命令给服务器来获取邮件,在POP3协议中,规定的命令也是几十条的,每条命令由命令和参数两部分组成都是以回车换行结束,并且命令和参数之间由空格分隔,命令通常也是由3-4个字母组成,参数最多可以为40个字符的长度,而服务器的响应信息是由一个状态码和可能附加信息的字符组成,所有的响应信息也是以回车换行结束的。状态码和其他协议定义的状态码有点不一样,POP3服务器响应的状态码有两种——“+OK”(确定)和”-ERR”(失败)。这样客户端可以通过检查响应的状态码所包含的字符来判断服务器是否响应客户端发送的命令,即响应信息中包含“+OK”表示成功响应,包含“-ERR”表示服务器未响应。同时在程序的实现中大家可以通过Debug来查看响应消息的组成,这样可以加深理解。

3.2.2 邮件接收的过程

 客户端从服务器接收邮件的过程主要经历3个状态:授权状态、操作状态和更新状态

(1)授权状态——客户端发送与POP3服务器的TCP连接请求,服务器接收后发送一个响应确认信息之后,此时客户端需要发送正确的用户名和密码进行确认,因为在邮件服务器上有很多用户邮箱,只有提供正确的用户名和密码才有权限访问自己的邮箱,就像现实生活中我们邮箱的钥匙一样的。

   发送用户名命令: USER mytest1989@sina.cn

 发送密码命令: PASS ******(这两个命令都在代码中有给出的,大家可以参考代码来理解邮件的接收过程)

(2) 操作状态——如果客户端提供了正确的用户名和密码,则授权状态也就通过了,就相当于打开了在服务器上自己的邮箱,现在用户就有权限进去下载,查看和删除邮件等操作的,然后在现实生活中的取邮件和删除邮件都很简单(只要打开了邮箱门,用手去拿就可以了),然后在网络应用上,这些操作都需要发送POP3命令给服务器,服务器接收到命令后再给出响应。操作中常用的命令有:

  •  STAT 命令——该命令从服务器中获取邮件总数和总字节数,服务器响应命令返回邮件总数和总字节数

 如:

客户端发送POP3命令: STAT
服务器响应命令: +OK 2 1340
服务器响应命令:

 

  • LIST 命令——该命令从服务器中获得邮件列表和大小,服务器响应命令返回列出邮件列表和大小。   

如: 

客户端发送POP3命令:LIST
服务器响应命令: +OK 2 message(1430 octect)
服务器响应命令:1 700
服务器响应命令:2 730
服务器响应命令:<一个空行>
  • RETR 命令—— 该命令从服务器中获得一个邮件,格式为 RETR <邮件编号>

如:

客户端发送POP3命令:RETR 1
服务器响应命令: 700 octets
服务器响应命令:<邮件头和内容>
服务器响应命令: <空行>
  • DELETE 命令——该命令告诉服务器将邮件标记为删除。(此时只是逻辑删除) 

 (3)更新状态——客户端发送QUIT命令后,此时就进入更新状态,POP3服务器释放在操作状态中取得的资源,并将逻辑删除的邮件进行物理删除,然后关闭与客户端的TCP连接。这样整个邮件处理的过程就结束了。                            

3.2.3 接收功能的实现代码

 有了前面接收邮件过程的介绍,再参考代码的实现,相信大家可以更好的理解客户端从POP3服务器中获取邮件的过程的,由于.net类库并没有帮我们封装POP3协议的实现类,要实现邮件的获取可以采用发送命令的方式,也可以使用Jmail组件,这个组件其实就是POP3协议的封装类,既然微软没有帮我们做,其他公司帮我们做好后来帮助我们简单的实现邮件的接收的一个类库罢了。然后在使用这个组件的过程中出现了好几个问题的,在源码中我都解释,大家可以下载源代码后查看的。

实现邮件接收的核心代码如下:

// 登录邮箱(这里是本程序——邮件收发器)
private void btnLogin_Click_1(object sender, EventArgs e)
{
// 与POP3服务器建立TCP连接
// 建立连接后把服务器上的邮件下载到本地
// 设置当前界面的光标为等待光标(就是我们看到的一个动的圆形)
Cursor.Current = Cursors.WaitCursor;

lsttbxStatus.Items.Clear();
try
{
// POP3服务器通过监听TCP110端口来提供POP3服务的
// 向POP3服务器发出tcp请求
tcpClient = new TcpClient(tbxPOP3Server.Text, 110);
lsttbxStatus.Items.Add(
正在连接…);
}
catch
{
MessageBox.Show(
连接失败, 错误, MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
lsttbxStatus.Items.Add(
连接失败!);
return;
}

// 连接成功的情况
networkStream = tcpClient.GetStream();
streamReader
= new StreamReader(networkStream, Encoding.Default);
streamWriter
= new StreamWriter(networkStream, Encoding.Default);
streamWriter.AutoFlush
= true;
string str;
// 读取服务器返回的响应连接信息
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add(
服务器拒接了连接请求);
return;
}
// 如果服务器接收请求
// 向服务器发送凭证——用户名和密码

// 向服务器发送用户名,请求确认
lsttbxStatus.Items.Add(核实用户名阶段…);
SendToServer(
USER + tbxUserMail.Text);
str
= GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add(
用户名错误.);
return;
}

// 用户名审核通过后再发送密码等待确认
// 向服务器发送密码,请求确认
SendToServer(PASS +txbPassword.Text);
str
= GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add(
密码错误!);
return;
}

lsttbxStatus.Items.Add(
身份验证成功,可以开始会话);
// 向服务器发送LIST 命令,请求获得邮件列表和大小
lsttbxStatus.Items.Add(获取邮件….);
SendToServer(
LIST);
str
= GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add(
获取邮件列表失败);
return;
}

lsttbxStatus.Items.Add(
邮件获取成功);

// 窗口控件控制
tabControlMyMailbox.Enabled = true;
btnReadMail.Enabled
= false;
btnDownLoad.Enabled
= false;
btnDeleteMail.Enabled
= false;

// 登陆成功后实例化邮件发送对象,以便后面完成发送邮件的操作
// 实例化邮件发送类(SmtpClient)对象
if (smtpClient == null)
{
smtpClient
= new SmtpClient();
smtpClient.Host
= tbxSmtpServer.Text;
smtpClient.Port
= 25;

// 不使用默认凭证,即需要认证登陆
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials
= new NetworkCredential(tbxUserMail.Text, txbPassword.Text);
smtpClient.DeliveryMethod
= SmtpDeliveryMethod.Network;
}

// 登陆成功后,自动接收新邮件
// 开始接收邮件
try
{
btnRefreshMailList.PerformClick();
}
catch
{
MessageBox.Show(
读取邮件列表失败!, 错误, MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}

lsttbxStatus.Items.Add(
登陆成功!);
lsttbxStatus.TopIndex
= lsttbxStatus.Items.Count – 1;
Cursor.Current
= Cursors.Default;

// 窗口控件控制
richtbxMailContentReview.Enabled = true;
tbxUserMail.Enabled
= false;
txbPassword.Enabled
= false;
btnLogin.Enabled
= false;
btnLogout.Enabled
= true;
tbxSmtpServer.Enabled
= false;
tbxPOP3Server.Enabled
= false;
btnReadMail.Enabled
= true;
btnDownLoad.Enabled
= true;
btnDeleteMail.Enabled
= true;
tabControlMyMailbox.Focus();
}

#region 处理与POP3服务器交互事件
// 获取服务器响应的信息
private string GetResponse()
{
string str = null;
try
{
str
= streamReader.ReadLine();
if (str == null)
{
lsttbxStatus.Items.Add(
连接失败——服务器没有响应);
}
else
{
lsttbxStatus.Items.Add(
收到:[" + str + "]);
if (str.StartsWith(-ERR))
{
str
= null;
}
}
}
catch(Exception err)
{
lsttbxStatus.Items.Add(
连接失败:[" + err.Message + "]);
}

return str;
}

// 检查响应信息
private bool CheckResponse(string responseString)
{
if (responseString == null)
{
return false;
}
else
{
if (responseString.StartsWith(+OK))
{
return true;
}
else
{
return false;
}
}
}

// 向服务器发送命令
private bool SendToServer(string str)
{
try
{
// 这里必须使用WriteLine方法的,因为POP3协议中定义的命令是以回车换行结束的
// 如果客户端发送的命令没有以回车换行结束,POP3服务器就不能识别,也就不能响应客户端的请求了
// 如果想用Write方法,则str输入的参数字符中必须包含“\r\n”,也就是回车换行字符串。
streamWriter.WriteLine(str);
streamWriter.Flush();
lsttbxStatus.Items.Add(
发送:[" + str + "]);
return true;
}
catch(Exception ex)
{
lsttbxStatus.Items.Add(
发送失败:[" + ex.Message + "]);
return false;
}
}

#endregion

 

3.3 程序运行结果演示

 首先输入邮箱名和密码登录到POP3服务器来获取邮件列表的演示:

 

然后在邮件列表中选中一个邮件进行阅读,然后进行回复邮件的操作演示(邮件的发送都可以附加附件发送出去):

阅读邮件的界面:

回复邮件的界面:

同时点击发送按钮后,就可以把邮件发送到sina的SMTP服务器上,再由新浪的SMTP服务器转发到QQ的SMTP服务器,QQ的POP3服务器中QQ的SMTP服务器获取收到的邮件,当QQ用户输入正确的邮箱名和密码后就可以从QQ的POP3服务器上获取收到的邮件。

点击发送按钮后成功发送邮件的图片:

四、总结

 介绍到这里,本专题的内容就已经介绍完了,希望通过本专题可以让大家明白邮件发送和接收的原理,并且可以自定义一个简单邮件收发器的功能的,在后面一专题将介绍FTP协议(文件传输协议),并实现一个简单的文件上传和下载的程序。

 

 

 

本文链接

博客园_首页

项目回顾之二:PhoneProtector 基于Android平台的手机隐私保护系统

      继上一篇博文《项目回顾之一:PlanBetter时间管理软件(基于Android平台)》之后,开始着手这篇博文的编写。

说在前面的

    首先,我想简单介绍一下这个项目的背景。项目名字有点长,叫做“PhoneProtector: 基于Android平台的手机隐私保护系统”。不理解的人可能会觉得是不是博主有点脑残,直接叫PhoneProtector不就好了。嗯,其实我也觉得取这么长的名字有点多余,但是这个项目为2012年全国大学生信息安全竞赛的参赛项目,名字至少要体现深度一点吧。除了完成一个项目以外,还需要写一篇竞赛作品报告,这个竞赛作品报告有点类似于论文。我们写了正好100页,洋洋洒洒2万多字的文档。先前都以为写好文档才是王道(当然作品也做得很认真,不含糊),连导师看完报告后觉得进决赛肯定没问题,可惜,这个项目出乎我们的意料没有进入决赛。后来,组长说可能是这个项目的名字不够霸气,如果把前面的”PhoneProtector”去掉可能会更有影响力,当然这只是玩笑话罢了。背景就介绍这么多。

 

项目需求

    这个作品是基于这么一种需求而制定的。我们在日常生活中可能会经常碰到这样的情况:在上班时,发现手机落在家中;下班回家后,发现手机落在了工作场所;在上课,手机落在寝室中等等,甚至更严重的是,手机丢失了。当遇到这些情况时,手机不在自己身边,除了无法使用之外,更重要的一点是担心手机隐私被泄漏。虽然Android手机平台自带有这样的保护措施,如屏幕手势加锁或者密码加锁,但是大多数用户不会考虑用这种功能,因为它在一定程度上阻碍了用户对手机的正常使用。因此,一旦手机远离了自己,甚至丢失或被盗之后,想再采取安全措施就已经太迟了。而且当手机丢失以后,最重要的不是手机本身,而是手机内的隐私信息,包括联系人信息,短信内容以及贮存在SD卡上的数据信息,一旦这些信息被别有用心的人利用,后果将不堪设想。简单来说,就是对手机丢失后的一种补救措施。
 
市场调研
      事实上,在我们构思从这个需求入手的时候,市场上已经有解决这类需求的应用出现了。例如著名的安全公司诺顿就出了一款Norton Security for android,它里边提供了一个功能就可以解决这个问题,通过设备远程锁定或数据清除。诺顿官方是这样介绍这个功能的:
      If your phone is lost or stolen, you can use another phone or program to send text message to your phone.Each text message should consist of an action word(lock or wipe) and a code of your choice. (For example, “wipe code123″)
      也就是说,就是在安装这款软件的时候,你要设置一个用来“加锁或格式化”设备的密码,如果你的手机丢了,那么用别人的手机发一条短信过去给手机加锁或者格式化,短信格式为“lock/wipe 密码”。但是这款软件有很多缺陷,比如别人捡到你的手机后就把这款应用删掉了,那你发短信就不起作用了;假如别人把你的卡换了怎么办?假如你加锁或格式化手机后,又在某一天拾到了你的手机,但是密码你又忘记了,你该怎么办?重刷系统?可能你会觉得本来丢手机的概率就很小了,发生这类情况的概率又是更小了,但是我们不排除这类情况出现的可能性。受到Murphy’s Law的启发,既然有这类情况发生的可能性,我们就有必要提出解决方案。当然,有的手机厂商在制造手机的时候,就把这类功能定制到了手机硬件和操作系统中,例如HTC sense,印象中是可以通过电脑的客户端查到手机的GPS位置。但本作品是面向所有Android用户的,不依赖于具体的手机硬件和操作系统。
 
作品特性  
      本作品的主要特性在于:
    1. 即使在手机不在身边的情况下,也能为手机用户提供最方便和迅速的隐私信息保护手段。方便是指用户可以利用SMS短信向手机发送操作指令,而迅速是指系统可快速执行用户指令和返回有用信息。
 
     2. 防护的全面性,一旦系统锁定手机,除非输入正确的口令,否则无法获取用户隐私信息。
 
    3. 系统本身的健壮性。独创Android进程间通信方式,通过守护进程机制来增强系统的抗击打能力,一般的手机应用可以轻松的被删除或卸载,然而,除非输入正确的口令,否则本系统将与用户的隐私信息一起存在于手机上,并正常工作。
 
     4. 数据的安全性,本系统涉及到的用户数据被存储在数据库中,而且敏感数据(比如密码)经过加密之后才存入数据库中,而非明文存储,因此更安全。
 
     5. 交互的友好性,无论是远程操作还是直接操作,针对每一种操作都会返回相应的信息提示,如果失败,也会根据情况返回失败信息。
 
系统功能
  本系统主要功能有短信远程操控、手机加锁解锁、手机当前位置信息获取、联系人信息备份、手机个人信息格式化、系统模式切换、用户信息设置和修改、SIM卡检测以及系统自保护。
   
下面对这些功能做具体介绍:

1.短信远程操控:当本系统处于保护模式时,系统激活后台程序部分,监控SMS短信内容,如果短信内容符合系统定义格式,则进行相应的操作,支持的操作有:锁定系统、获取当前位置信息、联系人信息备份以及手机个人信息清空,包括联系人、短信以及SD卡数据。这些操作都是通过利用普通的SMS短信来实现,无需特殊的数据传输。
 
    短信的格式为:[ PH 锁定密码 操作码 ]
 
   操作码分为01,02,03。01是锁定手机,02为备份联系人,03为格式化个人信息。03操作指令只有在信任号码发出的时候才会生效,否则被转为01操作。当操作码不正确的时候,默认转为01操作。每次短信操作时,只能使用一个号码,即忽略其他号码的短信。

2.手机加锁解锁:用户可以通过给本机发送短息,远程将手机加锁,加锁之后,除非输入解锁口令,否则本系统可以保证其他的一切操作都无法使手机解除锁定。本系统锁定模块的抗破坏性较强,可以防御一般的恶意解除手段,包括SIM卡替换、SD卡替换、USB数据传输以及关机重启等。在手机被锁定后,锁定界面只能进行:输入解锁密码、拨打发送锁定短信的号码和接听来点这三个操作。
 
    用户正确输入解锁口令后,系统解除手机锁定。系统保留一个信任号码,若用户忘记密码,则可以向信任号码发送短信,来获取密码。

3.当前位置信息获取:当系统被远程锁定之后,系统会向来信号码返回锁定完成确认以及手机当前位置信息,位置信息以经纬度坐标形式返回。

4.联系人信息备份:当手机丢失之后,对手机用户来说,最重要的就是联系人数据。无论手机是否被锁定,用户都可以将联系人信息备份至邮箱内。不同的是,正常使用情况下,用户通过系统主界面来执行备份,而当手机丢失或被盗后,用户可以通过短信远程执行联系人备份。

5.手机个人信息格式化:即清空手机上贮存的个人信息,包括联系人、通话记录、短信记录、SD卡数据(如下图)。由于本操作是不可逆操作,一旦用户执行,信息将永久地被删除,因此为了防止该操作被滥用,本系统要求只有信任号码发送的该操作指令才能被执行,否则任何其他的号码发送的该指令将被转成锁定指令。


6.系统模式切换:本系统共有两种模式供用户切换,分别为普通模式和保护模式,在普通模式下,系统只开启保护手机功能,而不开启自保护功能,系统也可以被用户正常卸载和删除。而在保护模式下,系统在后台启动监控服务,开启保护措施,当接收到执行指令时,就执行指令,包括备份联系人、锁定手机、格式化信息等,此时,本系统开启防卸载和数据删除功能(即系统自保护功能),应付企图使系统异常工作的动作。
 

7.用户信息设置和修改:由于本系统的工作依赖于用户的信息,具体的信息包括:锁定密码、解锁密码、信任号码以及电子邮箱。系统在每次运行的时候,检测这些信息是否完整,如果不完整则跳转到信息设置界面,直到用户输入完整信息。在信息修改界面中,用户可以输入相应的密码来修改这些信息。这些信息将在“系统数据说明”中具体介绍。
 
8.密码找回:当忘记密码的时候,用户可以通过该功能找回密码,该功能向信任号码发送一条包含密码的短信,用户也可以选择将密码发送到邮箱中,为防止此功能被滥用,系统设定每两次密码找回动作之间的时间间隔必须大于10分钟。
正常情况下,用户可以找回锁定密码和解锁密码,而当手机被锁定时,只能找回解锁密码。之所以这样设定,是在锁定情况下尽量减少与外界的数据交互。
 
9.SIM卡检测:应对手机SIM卡被非法替换或卸下的情况,如果开机时检测到SIM卡被替换,则调用锁定模块将手机锁定。
 
10.系统自保护:这是本作品的创新点之一。一般的手机防护应用,都可以通过系统的任务管理器轻松的卸载和数据清空,但是本系统如果处于安全模式下,就不会被轻易改动,不可以被停止、卸载和清空数据,增加了系统本身的鲁棒性和实用性。
 
系统结构

  上图即为本系统结构图,系统分为前台界面与后台保护服务,用户可以在前台界面执行相应的功能,例如模式切换、修改信息、联系人备份、密码找回等,后台保护服务主要有三个子系统,分别是手机锁定子系统、短信处理子系统以及防御子系统。

   前台界面与后台服务之间有数据交互,如密码找回需要通过短信处理系统来进行短信操作。手机锁定子系统负责将手机锁定,防止陌生人非法使用手机,短信处理子系统负责处理与短信有关的操作,包括短信拦截,发送短信,读取短信内容等,防御子系统负责系统本身的自保护,应对外界对本系统恶意的破坏,包括卸载、删除、强制停止以及清空数据等。

  远程手机可以通过发送SMS短信与系统进行交互,通过包含密码来执行相应的指令,如锁定、备份以及格式化等,在进行联系人信息备份时,系统需要通过数据交换,将联系人信息数据发送至邮件服务器。


最后传几张本软件的截图上来

 

注:解锁失败次数超过10次,“解锁锁定”按钮会被禁用


本文链接

博客园_首页