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寫的程式就是這樣的,古樸大方。。。。。