一個點到點的郵件系統(tǒng)設(shè)計
摘要:簡單介紹在多功能電話上實現(xiàn)一個點到點郵件收發(fā)系統(tǒng)的技術(shù)和過程。在通信方面,系統(tǒng)使用串口通信,通過Modem用電話號碼作為收發(fā)者的地址,避免網(wǎng)絡(luò)通信中需要IP地址的問題。在架構(gòu)上,系統(tǒng)分為服務層和客戶層:服務層負責通信鏈路的建立和數(shù)據(jù)的收發(fā),客戶層負責郵件管理和客戶界面,增加了系統(tǒng)的靈活性。由于Modem的傳輸速率有限,設(shè)計好的通信協(xié)議有助于提高郵件的收發(fā)速率,因此結(jié)合現(xiàn)有串口通信協(xié)議的特點,設(shè)計并實現(xiàn)TMP協(xié)議。通過該協(xié)議,郵件收發(fā)系統(tǒng)每秒可以收發(fā)2 KB的郵件數(shù)據(jù)。
關(guān)鍵詞:點到點 郵件系統(tǒng) 串口通信 AT指令
引 言
大量嵌入式設(shè)備已經(jīng)進入人們的日常生活和工作中,人們對嵌入式設(shè)備的要求越來越高,功能也越來越復雜。筆者與一個嵌入式硬件制造商合作,為其嵌入式電話產(chǎn)品建立了一個軟件系統(tǒng),包括操作系統(tǒng)、驅(qū)動程序和一系列應用軟件。其中點到點郵件收發(fā)系統(tǒng)是一個有明顯特色的應用。
本系統(tǒng)是一個基于串口通信的點到點郵件收發(fā)軟件。運行在嵌入式系統(tǒng)上的郵件收發(fā)程序通過Modem以撥號方式連上遠端的嵌入式系統(tǒng),然后進行郵件發(fā)送工作。系統(tǒng)結(jié)構(gòu)如圖1所示。
圖1Tmail系統(tǒng)結(jié)構(gòu)
從用戶角度看,其發(fā)送流程與普通的電子郵件差不多。用戶在編輯好郵件以后,在收信人一欄中填入對方的電話號碼,點擊發(fā)送,系統(tǒng)就會把郵件發(fā)送給對應電話號碼的遠端用戶處,當然遠端也必須接有同樣的嵌入式系統(tǒng)。
系統(tǒng)運行在基于ARM的S3C2410芯片嵌入式目標板上[1]。操作系統(tǒng)是定制的嵌入式Linux,圖形界面使用Qt/Embedded支持。
1 系統(tǒng)的構(gòu)架
系統(tǒng)主要由數(shù)據(jù)傳輸、郵件編碼、郵件管理和用戶界面四部分組成。由于要時刻監(jiān)視串口的狀態(tài),所以系統(tǒng)要一直處于運行狀態(tài)。但由于嵌入式系統(tǒng)的內(nèi)存資源和計算資源有限,要盡量減少程序運行時帶給整個系統(tǒng)的負載,采用了兩層的設(shè)計結(jié)構(gòu),分別叫作服務層和用戶層。服務層負責數(shù)據(jù)傳輸和郵件編碼,用C語言編寫,生成的可執(zhí)行文件較小,一直處于運行狀態(tài);用戶層負責郵件的管理和用戶界面,用C++配以Qt/Embedded編寫,由于有大量的界面元素,所以其可執(zhí)行文件較大,只有用戶需要查看和發(fā)送郵件時才運行。這樣就可以大大減少占用的資源,使整個系統(tǒng)具有更快的速度。
用戶層與服務層的通信分兩個方面:一方面,當用戶層運行時通過信號和共享內(nèi)存實現(xiàn)實時通信;另一方面,當用戶層沒有運行時,用文件暫存收到的郵件,在用戶層運行后,再由它通知用戶。
當需要發(fā)送郵件時,用戶層先把本地郵件拷貝到共享內(nèi)存中,然后發(fā)送信號通知服務層,服務層接收到信號后,把郵件取出、編碼和發(fā)送。在發(fā)送過程中,為了讓用戶看到發(fā)送的進度,服務端把狀態(tài)信息放在共享內(nèi)存中,然后發(fā)送信號通知用戶層更新狀態(tài)。當接收郵件時,若用戶層沒有運行,則把收到的郵件解碼后暫存到文件中;若用戶層運行著,則服務層通過共享內(nèi)存和信號不斷把接收進度傳遞給用戶層,使其顯示給用戶,但此時并不會把郵件數(shù)據(jù)傳給用戶層,因為此時的郵件數(shù)據(jù)是經(jīng)過編碼的,等所有數(shù)據(jù)都接收結(jié)束后,服務層才會把郵件解碼成本地郵件,然后再傳送給用戶層保存、顯示。
2 服務層的設(shè)計與實現(xiàn)
2.1 串口的初始化
在Linux中,串口的設(shè)備文件一般為/dev/ttyS0和/dev/ttyS1,分別表示串口1和串口2。首先要用open命令打開串口,然后根據(jù)具體的應用來配置串口,比如設(shè)置波特率、校驗方法、數(shù)據(jù)位、停止位和流控制等。
2.2 連接的建立和拆除
連接的建立過程,就如同一般的撥號上網(wǎng)過程。不同的只是,撥打的電話號碼不是ISP的號碼,而是收信人的號碼,因此需要在收信人方建立一個撥號服務器,使它能夠接通外來的連接請求。Linux下的pppd軟件可以實現(xiàn)撥號功能,mgetty可以用作撥號服務器。用pppd和mgetty來建立連接太過奢侈,因為只需建立物理上連通的鏈路,而不需要用PPP協(xié)議建立網(wǎng)絡(luò)層鏈路[2]。
本系統(tǒng)是用AT指令來編寫撥號和撥號服務器代碼的。AT指令是一組對Modem的操作指令,其中絕大多數(shù)指令都以AT開頭,以/r結(jié)尾[3]。用到的AT指令有:
① 初始化指令——AT;
② 撥號指令——ATDT電話號碼;
③ 應答指令——ATA;
④ 掛斷指令——+++ATH0。
工作流程為雙方打開串口并初始化后,都向Modem發(fā)送初始化指令(AT),如果返回“OK”,表示成功。成功后,接入方不斷讀取串口(1次/s)等待“RING”字符串。撥號方,通過撥號指令撥打?qū)Ψ诫娫捥柎a,并讀取串口等待“CONNECT”字符串。此時,接入方會在串口讀到“RING”,然后向串口發(fā)送應答指令(ATA),并讀取串口等待“CONNECT”字符串。此后,如果雙方都讀取到“CONNECT”,表示連接已經(jīng)建立,可以在此鏈路上傳輸數(shù)據(jù)。等數(shù)據(jù)傳輸完成后,撥號方用掛斷指令(+++ATH0)拆除連接。程序的流程如圖2所示。
圖2建立和拆除連接流程
2.3 數(shù)據(jù)傳輸協(xié)議
現(xiàn)在著名的串口協(xié)議主要有XMODEM和ZMODEM。XMODEM協(xié)議是一種使用撥號調(diào)制解調(diào)器的個人計算機通信中廣泛使用的異步文件運輸協(xié)議。這種協(xié)議以128字節(jié)塊的形式傳輸數(shù)據(jù),并且每個塊都使用一個校驗和過程來進行錯誤檢測。如果接收方關(guān)于一個塊的校驗和與它在發(fā)送方的校驗和相同時,接收方就向發(fā)送方發(fā)送一個認可字節(jié)。然而,這種對每個塊都進行認可的策略將導致低性能,特別是具有很長傳播延遲的衛(wèi)星連接的情況時,問題更加嚴重[4]。ZMODEM協(xié)議是XMODEM文件傳輸協(xié)議的一種增強形式,它不需要對每個塊都進行認可。事實上,它只是簡單地要求對損壞的塊進行重發(fā)。它不僅能傳輸更大的數(shù)據(jù),而且錯誤率更小。包含一種名為檢查點重啟的特性,如果通信鏈接在數(shù)據(jù)傳輸過程中中斷,能從斷點處而不是從開始處恢復傳輸[5]。
結(jié)合XMODEM和ZMODEM優(yōu)點,根據(jù)點到點郵件的特性,設(shè)計了一個稱之為TMP(Telephone Mail Protocol)的協(xié)議。
① 郵件發(fā)送方先發(fā)送一個郵件信息包(其中包括發(fā)送方的電話號碼和郵件長度),然后等待接收方應答。
② 接收方接收到郵件信息包后,發(fā)送應答包,等待郵件內(nèi)容包。
③ 發(fā)送方接收到應答包后,開始發(fā)送郵件內(nèi)容,直到郵件發(fā)送完畢或收到重發(fā)包。
④ 接收方接收郵件內(nèi)容包,并檢查其檢驗和,如有誤則發(fā)送重發(fā)包,否則一直接收。
⑤ 發(fā)送方若收到重發(fā)包,則根據(jù)重發(fā)包內(nèi)容重發(fā)部分郵件;若郵件發(fā)送完畢,還未收到重發(fā)包,則發(fā)送郵件結(jié)束確認包,要求接收方確認已正確接收到郵件。
⑥ 接收方接收到郵件結(jié)束確認包后,發(fā)送應答包,表示自己已正確接收到郵件。
⑦ 發(fā)送方接收到應答包后斷開連接。
⑧ 在此過程中,等待都有超時計時,一旦超時就會斷開連接;接收方接收到錯誤的包都會要求重發(fā),而如果發(fā)送方接收到錯誤的包則會馬上停止發(fā)送,斷開連接。
⑨ 包采用變長包,最大長度為128字節(jié),結(jié)構(gòu)為
其中:
◇ S為包起始符,取值為0x02,占1字節(jié);
◇ E為包結(jié)束符,取值為0x03,占1字節(jié);
◇ 包號本為占4字節(jié)的整型,但在傳輸過程中為了防止與包起始和結(jié)束符混淆,把它編成了BCD碼,占8字節(jié);
◇ 包類型也編成了BCD碼,占2字節(jié);
◇ 檢驗和是以字節(jié)為單位,把包號、包類型和數(shù)據(jù)累加起來,然后再編成BCD碼,占2字節(jié);
◇ 數(shù)據(jù)根據(jù)包的類型不同而不同,最多可以有114字節(jié)。
由于包的判斷只是依靠包起始符和結(jié)束符,包中的其他字段絕對不能與這兩個字符相同,所以對有些可能產(chǎn)生混淆字符的字段進行了編碼。
一共有五種類型的數(shù)據(jù)包:
① 郵件內(nèi)容包。包類型為0x00,數(shù)據(jù)是編碼的郵件片斷。
② 郵件信息包。包類型為0x81,數(shù)據(jù)是編成BCD碼的發(fā)送方電話號碼和郵件長度。
③ 郵件結(jié)束確認包。包類型為0x82,無數(shù)據(jù)。
④ 應答包。包類型為0x83,無數(shù)據(jù)。
⑤ 重發(fā)包。包類型為0x84,數(shù)據(jù)是編成BCD碼的新包號和重發(fā)起始位置。
2.4 包的發(fā)送和接收
包的發(fā)送和接收就是按照包的格式定義讀寫串口。郵件的收發(fā)過程就是依照TMP協(xié)議收發(fā)包的過程。
2.5郵件的編碼和解碼
本系統(tǒng)要求發(fā)送的郵件是可以帶附件的。在初始化串口時使用的是7位數(shù)據(jù),而附件內(nèi)容可能是二進制數(shù)據(jù),這和普通電子郵件碰到的問題一樣,要涉及到對郵件的編碼問題。在設(shè)計該系統(tǒng)前,已經(jīng)實現(xiàn)了普通電子郵件的客戶端軟件,所以就把普通電子郵件的編碼方法應用到這里,也就是點到點郵件也用MIME格式來編碼[6]。
3 客戶層的設(shè)計與實現(xiàn)
3.1 本地郵件的格式
本地郵件是指起草的、已發(fā)的或收到的存放在各信件箱中的郵件。
本地郵件可以有兩種存放方式:①按照MIME編碼,把編碼后的郵件放在郵箱中。②按照郵件的組成部分存放在郵箱中。第①種方式的好處是,郵件在發(fā)送前和接收后不需要編碼,另外也不需要備份附件文件,可以加快郵件發(fā)送和接收速度。但它有兩個最大的缺點,一是因為附件都會編入郵件中,需要大量的存儲空間;二是每次顯示郵件時,都需要把郵件解碼,保存時還要編碼,需要大量的計算資源。這兩點對于資源有限的嵌入式系統(tǒng)來說都是很大的問題。所以這里選擇了第②種方式。
本地郵件是以文本方式存放在文件中的。本地郵件由發(fā)件人、收件人、抄送人、已發(fā)送人、時間、標題、正文、回復地址和附件九部分組成。在文件中,除正文外其余八部分都各占1行。其中,抄送人和已發(fā)送人可以有多個,之間用分號隔開,附件部分的每個附件都有附件名和實際文件名組成。這兩個文件名間用斜杠隔開,而不同的附件用分號隔開。另外,正文放在郵件的最后,與上面八部分之間有一個空行格開,正文所占行數(shù)以正文內(nèi)容而定。一個本地郵件如下所示:
Date:2004-08-14
To:67165848
From: 67165762
Cc:67161234;67164321
Sent:67165848;67161234
Subject:peer to peer mailer Test
InReplyTo:67165762
Attachments:logo.png/20040814085700;face.png/200408145701
This is a Tmailer Test!
Hello World!
3.2 郵件箱文件的組織形式
一共有四個郵件箱,它們分別是:
① 草稿箱(draftbox)——存放起草了但尚未準備發(fā)送的郵件。
② 發(fā)件箱(sendbox)——存放起草完成,可以發(fā)送的郵件。
③ 已發(fā)郵件箱(sentbox)——存放已發(fā)送出去的郵件。
④ 收件箱(inbox)——存放收到的郵件。
每個郵件箱由兩個文件組成:郵件內(nèi)容文件和郵件索引文件。如草稿箱就有草稿郵件內(nèi)容文件draftbox和草稿郵件索引文件draftbox.index。
郵件內(nèi)容文件存放的是該郵件箱中的所有郵件,格式如本地郵件所示。郵件索引文件存放的是每個郵件的基本信息(在顯示郵件列表時要用的信息)及該郵件在內(nèi)容文件中存放的信息。一個郵件的索引結(jié)構(gòu)如下所示:
typedef struct {
chardate[40];// 起草、收到或發(fā)出的日期
charfrom[64];// 發(fā)件人或收件人
charsubject[140];// 標題
longsize;
// 郵件的大小(在內(nèi)容文件中所占的字節(jié)數(shù))
longposInFile;// 在內(nèi)容文件中的偏移
intflag;// bit0 = 已讀; bit1 = 是否有附件
}mail_summery_t;
由于索引文件較小,當顯示某一郵箱的郵件列表時,可以一次性把整個索引文件內(nèi)容裝入索引數(shù)組中,因此郵件列表的顯示可以不用讀取郵件內(nèi)容文件,完全取決于索引文件的內(nèi)容。添加郵件時,在內(nèi)容文件末尾添加郵件內(nèi)容,在索引數(shù)組中添加一個索引記錄;修改郵件時,不管原郵件在內(nèi)容文件中的數(shù)據(jù),在內(nèi)容文件末尾添加郵件內(nèi)容,修改該郵件索引記錄;刪除郵件時,只需刪除索引記錄即可。
索引文件的使用有兩大好處:一是在顯示郵件列表時不用從較大的內(nèi)容文件中提取想要顯示的信息;二是方便了郵件的訪問。可以實現(xiàn)郵件的隨機訪問,如果沒有索引文件,在每次修改和刪除郵件時,都需要移動大量的數(shù)據(jù)。現(xiàn)在,雖然會造成存儲空間的浪費,但這可以提高速度,對于資源有限的嵌入式系統(tǒng)很重要。此外,通過定時判斷郵件內(nèi)容文件的利用率可以壓縮內(nèi)容文件的大小,盡量減少空間的浪費。
3.3 附件的管理
在郵件中,附件較之郵件其他信息很大,所以郵件管理中附件的管理十分重要。
① 用戶在起草郵件并粘貼附件時要把附件文件備份出來,否則如果用戶不小心把附件文件刪除后再發(fā)送郵件,就不能發(fā)送該附件了。所以要特別開辟出一個文件夾來存放備份的附件。
② 用戶收到郵件并郵件解碼后,把其所帶的附件保存到特定的文件夾。
③ 用戶在刪除郵件時,也要刪除備份的附件,否則浪費空間。
④ 由于附件文件名有可能相同,所以備份附件時要使用惟一文件名。產(chǎn)生惟一文件名的方法是取當前時間字符串加上一個隨機值字符串。
3.4 郵件的抄送
本系統(tǒng)也實現(xiàn)了郵件的抄送功能,即一份郵件可以發(fā)給多個收信人。用戶層程序把郵件內(nèi)容及收信人列表(收件人+抄送人-已發(fā)送人)傳送給服務層,服務層程序根據(jù)收信人列表逐個發(fā)送郵件,并記錄發(fā)送成功的收信人。在給所有收信人都發(fā)送過后(當然不一定每個都成功發(fā)送),返回一個已發(fā)送成功的收件人列表給客戶層,客戶層把這個列表添加到本地郵件的sent字段中。當郵件的所有收信人,包括收件人和抄送人都收到郵件后,就認為該郵件發(fā)送完畢,把它移入已發(fā)送郵件箱。
3.5 界面的設(shè)計和實現(xiàn)
Tmailer主要有六個界面:
① 主菜單對話框。Tmailer運行后就是該界面。界面上有六個按鈕,分別用于起草郵件,進入4個郵件箱和退出程序。
② 郵件編輯對話框。用于編輯郵件,主要有收件人、抄送人、標題和正文4個編輯框。
③ 郵件查看對話框。用于查看收到的郵件,不能編輯。這個對話框上有兩個特殊按鈕,“轉(zhuǎn)發(fā)”按鈕和“回復”按鈕。當點擊“回復”按鈕時,Tmailer先會起草一篇新郵件,然后把當前郵件的InReplyTo填寫到新郵件的收件人欄中,把當前的正文稍作修改填寫到新郵件的正文中,然后就可以編輯和發(fā)送該新郵件了。當點擊“轉(zhuǎn)發(fā)”按鈕時,Tmailer也會先起草一篇新郵件,把當前的正文稍作修改填寫到新郵件的正文中,把當前附件粘貼到新郵件中,然后只要填上收件人就可以轉(zhuǎn)發(fā)郵件了。
④ 郵件箱對話框。用于顯示郵件列表和管理郵件。4個郵件箱共用該界面,不同的郵箱只是個別按鈕有所不同。當選中郵件按下回車鍵后,程序會根據(jù)當前所在的郵件箱選擇用郵件編輯對話框或郵件查看對話框顯示選中郵件。對于有附件的郵件,在每個郵件前都有一個標記標識。在收件箱中,未打開郵件會以粗體顯示,打開后就以正常字體顯示。
⑤ 附件編輯對話框:用于編輯(粘貼和刪除)附件,在郵件編輯對話框中點擊“附件“按鈕可以打開該對話框。
⑥ 附件查看對話框。用于查看和保存接收郵件的附件。如果查看的附件是圖像文件,Tmailer會通過Qt/Embedded的程序間通信機制把圖像信息傳遞給圖像顯示程序,讓它來顯示圖像。
結(jié)語
本文介紹了一個在嵌入式系統(tǒng)上點到點郵件系統(tǒng)的設(shè)計和實現(xiàn),重點講述了如何在計算、存儲資源有限的特殊環(huán)境下,來設(shè)計一個點到點通信軟件;如何運用AT指令操作Modem;對具體的應用如何來有效地設(shè)計串口的通信協(xié)議和如何對郵件進行合理有效的管理。本系統(tǒng)已經(jīng)成功地運行在一款多功能電話上,將來通過擴展還可以增強群發(fā)、定時發(fā)送等功能。
參考文獻
1 Samsung Electronics. S3C2410X 32?Bit Risc Microprocessor User's Manual Revision 1.2, 2003
2 Robert Hart. PPP Howto. http://www.faqs.org/docs/LinuxHowto/PPPHowto.html, 1997
3 Michael R. Sweet. Serial Programming Guide for POSIX Operating Systems(5th Edition). http://digilander.libero.it/robang/rubrica/serial.htm,1999
4 Richard Stevens W. Unix環(huán)境高級編程. 北京:機械工業(yè)出版社, 2000
5 Matt Welsh. Linux權(quán)威指南. 第3版. 北京:中國電力出版社, 2000
金震江:碩士研究生,主要研究方向為嵌入式系統(tǒng)。呂強:教授,主要研究方向為計算機操作系統(tǒng)、分布式計算、計算語言學等。褚亞銘:碩士研究生,主要研究方向為嵌入式操作系統(tǒng)。楊季文:教授,主要研究方向為計算機中文信息處理技術(shù)、計算機操作系統(tǒng)。