首頁 >php教程 >PHP开发 >用Socket發送電子郵件(php)

用Socket發送電子郵件(php)

高洛峰
高洛峰原創
2016-12-02 10:12:151180瀏覽

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 : 告訴伺服器發信人的位址 

MAIL FROM: sender_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: chatme@263.net 
250 Ok🜎RCPT TO DATA 
354 End data with . 
To: chatme@263.net 
From: chatme@263.net 
Subject: test 
From: chatme@263.testcom 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

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)) 

{ lc gethostbyaddr("localhost"); 

else 
$this->welcome=$welcome; 
$this->debug=$debug; 
$this->lastmessage=""+; 
$this->lastmessage="";> ; 
$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 } 
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行將訊息的前面加上"    ">> "來區別上傳指令。 
第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 ); 
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)。

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

郵件發送處理

  下面是真正的秘密了,可要看仔細了。 :)

------------------------------------------------- ------------------------------- 
1 函數send($to,$from,$subject,$message) 
2 { 

4 //連接伺服器 
5 $this->lastact="connect"; 

7 $this->show_debug("連接到 SMTP 伺服器:".$this->smtp, "out"); 
8 $this->fp = fsockopen ( $this->smtp, $this->port ); 
9 if ( $this->fp )
10 { 
11 
12 set_socket_blocking( $this->fp, true ); 
13 $this->lastmessage=fgets($this->fp,512); 
14 $this->show_debug($this->lastmessage, "in"); 
15 
16 if (!ereg ( "^220", $this->lastmessage ) )
17 { 
18 回傳 false; 
19 } 
20 其他
21 { 
22 $this->lastact="HELO " . 「n」; 
23 if(!$this->do_command($this->lastact, "250")) 
24 { 
25 fclose($this->fp); 
26 回 false; 
27 } 
28 
29 $this->lastact="MAIL FROM: $from" . 「n」; 
30 if(!$this->do_command($this->lastact, "250"))
31 { 
32 fclose($this->fp); 
33 回 false; 
34 } 
35 
36 $this->lastact="RCPT TO: $to" . 「n」; 
37 if(!$this->do_command($this->lastact, "250")) 
38 { 
39 fclose($this->fp); 
40 回傳 false; 
41 } 
42 
43 // 傳送正文 
44 $this->lastact="DATAn"; 
45 if(!$this->do_command($this->lastact, "354"))
46 { 
47 fclose($this->fp); 
48 回傳 false; 
49 } 
50 
51 //處理Subject頭 
52 $head="Subject: $subjectn"; 
53 if(!empty($subject) && !ereg($head, $message)) 
54 { 
55 $message = $head.$message; 
56 } 
57 
58 //處理From頭 
59 $head="From: $fromn"; 
60 if(!empty($from) && !ereg($head, $message)) 
61 { 
62 $message = $head.$message; 
63 } 
64 
65 //處理To頭 
66 $head="To: $ton"; 
67 if(!empty($to) && !ereg($head, $message)) 
68 { 
69 $message = $head.$message; 
70 } 
71 
72 //加上結束字串
73 if(!ereg("n.n", $message)) 
74 $message .= "n.n"; 
75 $this->show_debug($message, "out"); 
76 fputs($this->fp, $message); 
77 
78 $this->lastact="QUITn"; 
79 if(!$this->do_command($this->lastact, "250"))
80 { 
81 fclose($this->fp); 
82 回傳 false; 
83 } 
84 } 
85 回傳 true; 
86 } 
87 else
88 { 
89 $this->show_bug("連線失敗! 
90 回傳false; 
91 } 
92 } 
-------------------------------------- -------------- ------------------------------------ -------------- ----------------- 
凡有些意思很清楚的我就不多說了。四個參數,分別是$to表示收信人,$from表示發信人,$subject表求郵件主題和$message

表示郵件體。

第8行,連接郵件伺服器,如果成功回應碼應為220。 
第12行,設定阻塞模式,表示訊息必須回傳才能繼續。詳細說明看手冊吧。 
第16行,判斷回應碼是否為220,如果是,則繼續處理,否則出錯回傳。 
第22-27行,處理HELO指令,期望回應碼為250。 
第29-34行,處理MAIL FROM指令,期望回應碼為250。 
第36-41行,處理RCPT TO指令,期望回應碼為250。 
第44-49行,處理DATA指令,期望回應碼為354。 
第51-76行,產生郵件體,並發送。 
第52-56行,如果$subject不為空,則查找郵件體中是否有主題部分,如果沒有,則加上主題部分。 
第59-63行,如果$from不為空,則查找郵件體中是否有發信人部分,如果沒有,則加上發信人部分。 
第66-70行,如果$to不為空,則查找郵件體內是否有收信人部分,如果沒有,則加上收信人部分。 
第73-74行,查找郵件體是否有了結束行,如果沒有則加上郵件體的結束行(以"."作為單獨的一行的特殊行)。 
第76行,發送郵件體。 
第78-83行,執行QUIT結否與伺服器的連接,期望回應碼為250。 
第85行,回傳處理成功標誌(true)。 
第81-91行,與伺服器連線失敗的處理。

  以上為整個send_mail類別的實現,應該不是很難的。下面給出一個實例。

郵件發送實例 
  先給出一個最簡單的實例: 
---------------------------------- ---------------------------------------------- 
 
1 include "sendmail.class.php3"; 
2 $email="Hello, this is a test letter!"; 
3 $sendmail=new send_mail("smtp.263.net", "limodou", true); / /顯示傳輸訊息 
4 if($sendmail->send("chatme@263.net", "chatme@263.net", "test", $email)) 
5 { 
6 echo "發送成功!"; 
7 } 
8 else 
9 { 
10 echo "寄送失敗! -------------------------------------------------- ------------ 
第1行,裝入send_mail類別。 
第3行,建立一個類別的實例,且設定顯示調節訊息,如果不想顯示,可以 
    $sendmail=new send_mail("smtp.263.net");。 
第4行,發送郵件。


很簡單,不是嗎?下面再給合以前的發送MIME郵件的例子,給一個發送HTML附件的例子。

------------------------------------------------ -------------------------------- 

include "MIME.class.php3"; 
//註,在發送MIME郵件一文中,這個類別文件名為MIME.class,在此處我改成這樣的

$to = 'chatme@263.net'; //改為收信人的郵箱 
$str = "Newsletter for ".date('M Y', time());

//訊息被我改少了 
$html_data = '

'. $str. ' title>  

Hello! This is a test! 
 
';

//產生MIME類實例 
$mime = new MIME_mail ("chatme@263.net", $to, $str);

//新增HTML附件 
$mime->attach($html_data, "", HTML, BASE64);

//註解掉,採用我的寄件郵件處理 
//$mime->send_mail();

//產生郵件 
$mime->gen_email();

//顯示郵件訊息 
//echo $mime->e. >";

//包含sendmail文件 
include "sendmail.class.php3";

//建立實例 
$sendmail=new send_mail("smtp.263.net", "limodou$sendmail=new send_mail("smtp.263.net", "limodou", true); //寄email 

$sendmail->send("chatme@263.net", "chatme@263.net", $str, $mime->email);


?>


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn