2009年10月31日

PHP中用Socket發送電子郵件

在作者所申請的幾個PHP 主頁空間中,能夠提供mail功能的實在不多,總是調用完mail()函數之後就毫無下文了。

但是電子郵件在網上生活中的作用越來越大作用我不想再說了,但是如果主頁空間不支持mail()發送那麼怎麼辦呢?我也想過通過socket來實現郵件 發送,但無奈對用php 進行socket編程不熟悉,再加上發送郵件要用到SMTP協議,又要讀不少的英文了,所以一直也沒有去研究過。

終於有一天我發現了一篇文章,關於用socket編程發送郵件。我如獲至寶般將其拷貝下來,並且將其改造成了一個php可用的類,供大家使用。

原來的文章只是一個簡單的例子,而且還有一些錯誤,在我經過多次的實驗、改造終於將其改成了一個直接使用socket,向指定的郵箱發送郵件的類,如果大家和前面關於發送MIME的文章結合起來,就可以實現在不支持mail()函數的網站上發送郵件了。

因為發送郵件的過程需要時間,可能與mail()的處理機制還不完全一樣,所以速度要慢一些,但是可以解決需要發送郵件功能的燃眉之急,同時你也可以學習用php 進行socket編程。下面就將這個類的實現原理介紹給大家,同時向大家講解一些關於SMTP的基本知識。

Socket編程介紹

向大家申明,本人不是一個TCP/IP編程專家,故在此只是講出了我的一點理解和體會;使用fsockopen函數打開一個Internet連接,函數語法格式:

int fsockopen(string hostname, int port, int [errno], string [errstr], int [timeout]);

參數的意思我想不用講了,這裡由於要使用SMTP協議,所以端口號為25。在打開連接成功後,會返回一個socket句柄,使用它就可以像使用文件句柄一樣的。可使用的操作有fputs(),fgets(),feof(),fclose()等,很簡單地介紹就到這裡吧。

SMTP的基礎

基於TCP/IP的因特網協議一般的命令格式都是通過請求/ 應答方式實現的,採用的都是文本信息,所以處理起來要容易一些。SMTP是簡單郵件傳輸協議的簡稱,它可以實現客戶端向服務器發送郵件的功能。

所以下面所講的命令是指客戶端向服務器發出請求指令,而響應則是指服務器返回給客戶端的信息;SMTP分為命令頭和信息體兩部分。命令頭主要完成客戶端與服務器的連接,驗證等。整個過程由多條命令組成。每個命令發到服務器後,由服務器給出響應信息,一般為3 位數字的響應碼和響應文本。不同的服務器返回的響應碼是遵守協議的,但是響應正文本則不必。每個命令及響應的最後都有一個回車符,這樣使用fputs()和fgets()就可以進行命令與響應的處理了。SMTP的命令及響應信息都是單行的。信息體蚴怯始?的正文部分,最後的結束行應以單獨的"."作為結束行。

客戶端一些常用的SMTP指令為:

HELO hostname: 與服務器打招呼並告知客戶端使用的機器名字,可以隨便填寫
MAIL FROM: sender_id : 告訴服務器發信人的地址
RCPT TO: receiver_id : 告訴服務器收信人的地址
DATA : 下面開始傳輸信件內容,且最後要以只含有.的特殊行結束
RESET: 取消剛才的指令,從新開始
VERIFY userid: 校驗帳號是否存在(此指令為可選指令,服務器可能不支持)
QUIT : 退出連接,結束

服務器返回的響應信息為(格式為:響應碼+空格+解釋):

220 服務就緒(在socket連接成功時,會返回此信息)
221 正在處理
250 請求郵件動作正確,完成(HELO,MAIL FROM,RCPT TO,QUIT指令執行成功會返回此信息)
354 開始發送數據,結束以 .(DATA指令執行成功會返回此信息,客戶端應發送信息)
500 語法錯誤,命令不能識別
550 命令不能執行,郵箱無效
552 中斷處理:用戶超出文件空間

下面給出一個簡單的命令頭(這是在打開socket之後做的),是我向stmp.263.net發郵件的測試結果:

HELO limodou
250 smtp.263.net
MAIL FROM: [email protected]
250 Ok
RCPT TO: [email protected]
250 Ok
DATA
354 End data with .
To: [email protected]
From: [email protected]
Subject: test
From: [email protected]
test
.
QUIT
250 Ok: queued as C46411C5097E0

這就是一些SMTP的簡單知識。相關內容可以查閱RFC。
RFC 821定義了收/發電子郵件的相關指令。
RFC 822則制定了郵件內容的格式。
RFC 2045-2048制定了多媒體郵件內容的格式,
RFC 1113, 1422-1424則是討論如何增進電子郵件的保密性。


send_mail類的實現

現在開始介紹我所編寫的發送郵件類。有了上面的預備知識了,下面就是實現了。

類的成員變量

var $lastmessage; //記錄最後返回的響應信息
var $lastact; //最後的動作,字符串形式
var $welcome; //用在HELO後面,歡迎用戶
var $debug; //是否顯示調試信息
var $smtp; //smtp服務器
var $port; //smtp端口號
var $fp; //socket句柄

其中,$lastmessage和$lastact用於記錄最後一次響應信息及執行的命令,當出錯時,用戶可以使用它們。為了測試需要,我還定義了$debug變量,當其值為true時,會在運行過程中顯示一些執行信息,否則無任何輸出。$fp用於保存打開後的socket句柄。

類的構造
--------------------------------------------------------------------------------
function send_mail($smtp, $welcome="", $debug=false)
{
if(empty($smtp)) die("SMTP cannt be NULL!");
$this->smtp=$smtp;
if(empty($welcome))
{
$this->welcome=gethostbyaddr("localhost");
}
else
$this->welcome=$welcome;
$this->debug=$debug;
$this->lastmessage="";
$this->lastact="";
$this->port="25";
}
--------------------------------------------------------------------------------
這個構造函數主要完成一些初始值的判定及設置。$welcome用於HELO指令中,告訴服務器用戶的名字;HELO指令要求為機器名,但是不用也可以。如果用戶沒有給出$welcome,則自動查找本地的機器名。

顯示調試信息
--------------------------------------------------------------------------------
1 function show_debug($message, $inout)
2 {
3 if ($this->debug)
4 {
5 if($inout=="in") //響應信息
6 {
7 $m='<< ';
8 }
9 else
10 $m='>> ';
11 if(!ereg("\n$", $message))
12 $message .= "";
13 $message=nl2br($message);
14 echo "${m}${message}";
15 }
16 }
--------------------------------------------------------------------------------
這個函數用來顯示調試信息。可以在$inout中指定是上傳的指令還是返回的響應,如果為上傳指令,則使用"out";如果為返回的響應則使用"in"。

第3行,判斷是否要輸出調試信息。

第5行,判斷是否為響應信息,如果是,則在第7行將信息的前面加上"<< "來區別信息;否則在第10行加上">> "來區別上傳指令。

第11-12行,判斷信息串最後是否為換行符,如不是則加上HTML換行標記。第13行將所以的換行符轉成HTML的換行標記。

第14行,輸出整條信息,同時將信息顏色置為灰色以示區別。

執行一個命令
--------------------------------------------------------------------------------
1 function do_command($command, $code)
2 {
3 $this->lastact=$command;
4 $this->show_debug($this->lastact, "out");
5 fputs ( $this->fp, $this->lastact );
6 $this->lastmessage = fgets ( $this->fp, 512 );
7 $this->show_debug($this->lastmessage, "in");
8 if(!ereg("^$code", $this->lastmessage))
9 {
10 return false;
11 }
12 else
13 return true;
14 }
--------------------------------------------------------------------------------
在編寫socket處理部分發現,一些命令的處理很相似,如HELO,MAIL FROM,RCPT TO,QUIT,DATA命令,都要求根據是否顯示調試信息將相關內容顯示出來,同時對於返回的響應碼,如果是期望的,則應繼續處理,如果不是期望的,則應中斷出理。所以為了清晰與簡化,專門對這些命令的處理編寫了一個通用處理函數。函數的參數中$code為期望的響應碼,如果響應碼與之相同則表示處理成功,否則出錯。

第3行,記錄最後執行命令。

第4行,將上傳命令顯示出來。

第5行,則使用fputs真正向服務器傳換指令。

第6行,從服務器接收響應信息將放在最後響應消息變量中。

第7行,將響應信息顯示出來。

第8行,判斷響應信息是否期待的,如果是則第13行返回成功(true),否則在第10行返回失敗(false)。

這樣,這個函數一方面完成指令及信息的發送顯示功能,別一方面對返回的響應判斷郵件發送處理是否成功。

原文出處:http://calos-tw.blogspot.com/2009/09/phpsocket.html

沒有留言:

聖誕跑趴歡樂降★韓版假兩件針織連衣裙$890,新款顯瘦棒球服連帽風衣外套下殺$510,冬新款歐美棉襖棉服連帽短款外套限量↘$530,秋冬新款韓版大碼毛領氣質毛料外套↘$680,快上左耳貓網路購物

您好!如圖太小看不清楚,請 點選此處 看詳細內容 左耳貓蝦皮賣場 https://shopee.tw/hrf5168 左耳貓 露天賣場 http://class.ruten.com.tw/user/index00.php?s=starbox 左耳貓粉...