我的邮箱客户端程序Popmail
05年的时候写了一个邮箱客户端程序。当时主要目的是研究POP3和SMTP协议,同时锻炼自己的网络编程能力。当然了,如果自己写的邮箱客户端能够满足自身的日常工作需要,而不是频繁的登录不同的网页邮箱,那就再好不过了。时隔16年,给popmail增加了SSL(TLS 1.2)会话,感觉安全了一点,邮件再也不用裸奔了,看到16年前的代码,非常感慨,随便写写,特此纪念。
POP3和SMTP这两个协议本身都很简单,但实现起来还是有很大难度,尤其是你希望把它写的健壮、易用或者产品化的时候。
比如HTTP协议也很简单,写个简单的socket程序通过GET命令就能把网页给down下来。但接收大的网络资源就复杂多了。何时解析、如何解析完整的HTTP响应头,就是个头疼问题。因为你不能指望一次recv就能接收完所有响应数据,也不能指望服务器先发送HTTP响应头,然后再发送响应数据。只有把HTTP响应头彻底解析了,我们才能知道后续接收的Body数据有多大,何时才能接收完毕。
比如通过响应头的"Content-Length"字段,才能知道后续Body的大小。这个大小可能超过了你之前开辟的接收数据缓存区大小。当然你可以在得知Body大小后,重新开辟一个与"Content-Length"一样大小的缓存区。但这样做显然是不明智的,比如你get的是一部4K高清蓝光小电影,蓝光电影不一定能get到,蓝屏电脑倒有可能get到。。。。。。
遇到服务器明确给出"Content-Length"字段,是一件值得额手称庆的大喜事,但不是每个IT民工都这么幸运。如果遇到的是不靠谱的服务器,发送的是"Transfer-Encoding: chunked",那你就必须锻炼自己真正的解析和组织能力了。这些分块传输的数据,显然不会以你接收的节奏到达你的缓冲区,比如先接收到一个block块大小,然后是一个完整的块数据,很有可能你会接收到多个块或者不完整的块,这就需要你站在宏观的角度把他们拼接起来。
如果你遇到的是甩的一米的服务器,它不仅给你的是chunked,而且还增加了"Content-Encoding: gzip",那么你就需要拼接后进行解压,当然你也可能遇到的是"deflate"压缩。
题外话:我一直困惑的是HTTP协议为何不是对分块数据单独gzip压缩然后传输,而只能是整体gzip压缩后再分块传输。这个对大资源传输很关键,比如上面的4K高清蓝光小电影,显然不能通过gzip+chunked方式传输,土豪服务器例外。
当然你也可以用开源的llhttp来解析收到的http数据,从而避免上述可能会遇到的各种坑。最新版本的nodejs中就使用llhttp代替之前的的http-parser,据说解析效率有大幅提升。为此我下载了nodejs源码,并编译了一把,这是一个快乐的过程,因为你可以看到v8引擎,openssl,zlib等各种开源库。。。。,不过llhttp只负责解析,不负责缓存,因此你还是需要在解析的过程中,进行数据缓存。这是我写的通过llhttp解析http响应数据的案例:
基于SSL(TLS)的HTTPS网页下载——如何编写健壮的可靠的网页下载
这里面有我封装好的sslite.dll库可以方便的帮助你进行SSL客户端通信,目前支持TLS1.2,我的popmail因为使用sslite库才把衣服穿上避免了裸奔。
花开两朵各表一枝,还是回到POP3/SMTP上来。
相较而言,实现POP3要比实现SMTP复杂,这个复杂不是指协议本身有多复杂,也不是POP3比SMTP多了几个命令,而是指用代码实现协议的难度,尤其是解析难度。POP3是接收协议,SMTP是发送协议,总体而言发送比接收要简单很多。
因为发送数据的时候你可以耍流氓,不管服务器的死活,可以变态的1个字节1个字节发数据,也可以忽长忽短的发数据,从而让服务器猜不透你,直到把数据全部发送完毕。但接收数据就复杂多了,比如上面提到的如何接收HTTP响应数据,现在需要你来面对猜不透的服务器了,因为服务器也可能耍流氓。
早期的email协议只支持ASCII码这种纯文本传输,后来随着富文本的出现,图像、文件也迫切需要通过email进行传输。这时MIME协议诞生了,MIME的出现更多的是一种向下兼容的无奈,而不是革命。通过对二进制数据或非ASCII码数据进行base64或quoted-printable编码,来实现纯ASCII码的传输。显然这种方式会让你的邮件体变大,传输效率下降。
传输下降只是一方面,解析MIME格式的邮件也是一件头疼的事,对于"Content-Type: multipart/mixed"来说,需要根据boundary值,将不同mixed part邮件体给解析出来。同时你要面临各种编码格式,否则你的邮件标题、附件名称就会出现乱码,比如这种:"=?gb2312?B?=",比如这种:"=?gb2312?Q?",又比如这种"=?unicode-1-1-utf-7?q?"。。。。,当时还还遇到过"Content-type: message/rfc822",或者"content-disposition : inline"字段,完全处于懵B状态。05年的时候,网上资料特别少,也不认识张小龙,张小龙不仅是微信之父,更是Foxmail之母。如果当时能联系上,可能会发微信咨询一下,或者qq加好友哦。。。。。。,但在当年只能连蒙带猜,遇到一次乱码就猜测着解析看看,敢于在踩雷中解析,在解析中踩雷。
当年写邮箱客户端使用的是经典的VC6.0,也许很多90后、00后、10后没有听说过它,没关系,没关系,下面就是用VC6编写的popmail,不高调,不奢华,一眼就能看出vc6的影子。它支持多邮箱账户、邮件自动接收、快速查看邮件列表、远程删除邮件,自动回复等人民群总喜闻乐见却基本不用的功能。
1、主界面
2、多邮箱账户配置
3、账户配置
4、自动规则设定
5、接收邮件
6、快速查看
为什么不能把界面写的多次多彩呢?VC6写的程序就是这样的,古朴大方。。。。。
我的邮箱客户端程序Popmail的更多相关文章
- 2015第37周二foxmail邮箱客户端迁移
foxmail7.0邮箱客户端迁移风波浪费我下午不少时间,不知为何做完foxmail客户端在卡的时候我将其强制关闭,然后将整个邮箱目录拷贝到一台新电脑上,运行客户端居然我要新建账户(账户信息丢失),将 ...
- javaWeb之邮箱发送(邮箱客户端配置)
这里使用的是本机的邮箱服务器 , 代码执行条件: 1.·邮箱服务器 , 下载地址 密码 s4xn 邮箱服务器配置: 1):安装 2):打开服务器 红色部分是默认账号,不用处理 3)系统设置 ...
- VC++ 使用ShellExecute函数调用邮箱客户端发送邮件(可以带附件)
之前写过一篇博文,通过MAPI实现调用邮箱客户端发送邮件带附件,当时对ShellExecute研究不深,以为ShellExecute不能带附件,因为项目需求原因(MAPI只能调用Foxmail和O ...
- VC++使用IMAPI调用Outlook邮箱客户端和Foxmail邮箱客户端遇到的问题
http://www.cnblogs.com/abiao/articles/303090.html 发送邮件 MAPISendMail() 发送邮件功能就是对MAPISendMail()的封装.下面解 ...
- 基于 SailingEase WinForm Framework 开发客户端程序(3:实现菜单/工具栏按钮的解耦及状态控制)
本系列文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以 SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...
- php编写tcp服务器和客户端程序
这是我从别的地方看到的. 1.修改php.ini,打开extension=php_sockets.dll 2.客户端程序 SocketClient.php <?php set_time_limi ...
- 在公司内网上创建自己的 OSM.Planet 街道级别地图服务器及其客户端程序
转自我的BLOG http://blog.csdn.net/goldenhawking/article/details/6402775 最近经过陛下点拨,涉猎了“OpenStreetMap”,做了不 ...
- 使用 Socket 通信实现 FTP 客户端程序(来自IBM)
FTP 客户端如 FlashFXP,File Zilla 被广泛应用,原理上都是用底层的 Socket 来实现.FTP 客户端与服务器端进行数据交换必须建立两个套接字,一个作为命令通道,一个作为数据通 ...
- winform客户端程序第一次调用webservice方法很慢的解决方法
.net2.0的winform客户端最常用的与服务端通信方式是通过webservice,最近在用dottrace对客户端做性能测试的时候发现,客户端程序启动以后,第一次调用某一个webservice的 ...
随机推荐
- 百胜中国使用Rainbond实现云原生落地的实践
百胜中国使用Rainbond实现云原生落地的实践 关于百胜中国 自从1987年第一家餐厅开业以来,截至2021年第二季度,百胜中国在中国大陆的足迹遍布所有省市自治区,在1500多座城镇经营着11023 ...
- 联盛德 HLK-W806 (五): W801开发板上手报告
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 力扣 - 剑指 Offer 46. 把数字翻译成字符串
题目 剑指 Offer 46. 把数字翻译成字符串 思路1(递归,自顶向下) 这题和青蛙跳台阶很类似,青蛙跳台阶说的是青蛙每次可以跳一层或者两层,跳到第 n 层有多少种解法,而这题说的是讲数字翻译成字 ...
- 洛谷 P4900 - 食堂(推式子)
洛谷题面传送门 首先推式子: \[\begin{aligned} ans&=\sum\limits_{i=A}^B\sum\limits_{j=1}^i\{\dfrac{i}{j}\} \en ...
- mysql—mysql查询语句提示Unknown column ‘xxx’ in ‘where clause’
运行结果中提示Unknown column 'xxx' in 'where clause'的问题.经过大神的指导,顿时明白其中缘由,如果sql中定义的类型是int型的可以不用加引号,但是如果是字符串类 ...
- .Net Core——用SignalR撸个游戏
之前开内部培训,说到实时web应用这一块讲到了SignalR,我说找时间用它做个游戏玩玩,后面时间紧张就一直没安排.这两天闲了又想起这个事,考虑后决定用2天时间写个斗D主,安排了前端同学写客户端,我写 ...
- 学习java 7.15
学习内容: 进程:正在运行的程序 是系统进行资源分配和调用的独立单位 每个进程都有它自己的内存空间和系统资源 线程:是进程中的单个顺序控制流,是一条执行路径 单线程:一个进程如果只有一条执行路径,则称 ...
- 【leetcode】337. House Robber III
The thief has found himself a new place for his thievery again. There is only one entrance to this a ...
- Output of C++ Program | Set 9
Predict the output of following C++ programs. Question 1 1 template <class S, class T> class P ...
- Advanced C++ | Conversion Operators
In C++, the programmer abstracts real world objects using classes as concrete types. Sometimes it is ...