ソケットを使用して電子メールを送信する
著者が申請したいくつかの PHP ホームページ スペースの中で、mail() 関数を呼び出しても何も起こらないメール機能を提供できるものは多くありません。しかし、オンライン生活において電子メールの役割はますます高まっています。考えてみてください。電子メールを受信せずにインターネットをサーフィンする場合、インターネットのバグは本当のインターネットのバグと言えるでしょうか?メール
の役割についてはもう話したくないのですが、ホームページスペースがmail()送信に対応していない場合はどうすればよいでしょうか?ソケット経由でメールを送信することも考えましたが、残念ながら私は PHP を使用したソケット プログラミングに慣れていません。また、メールを送信するには SMTP プロトコルが必要であり、大量の英語を読む必要があるため、まだ勉強したことがありません。ついにある日、ソケット プログラミングを使用して電子メールを送信することに関する記事を見つけました。私は宝物を見つけたかのようにそれをコピーし、誰でも使用できる PHP で利用できるクラスに変換しました。元の記事は単なるサンプルであり、いくつかのエラーがあり、多くの実験と修正を経て、最終的にはソケットを直接使用して指定されたメールボックスにメールを送信するクラスに変更しました。 たとえば、以前の記事を結合する場合です。 MIME を送信すると、mail() 関数をサポートしていない Web サイトでも電子メールを送信できます。
メールの送信処理には時間がかかるため、mail()の処理機構と全く同じではないため、速度は遅くなりますが、同時に
メールを送信したいという緊急のニーズを解決できます。ソケットプログラミングにphpを使用する方法も学ぶことができます。次に、このクラスの実装原理を紹介し、SMTP に関する基本的な知識についても説明します。
ソケット プログラミングの概要
私は TCP/IP プログラミングの専門家ではないので、ここでは私の理解と経験を共有しているだけであることを皆さんに指摘したいと思います。
fsockopen 関数を使用してインターネット接続を開きます。関数の構文形式は次のとおりです。
int fsockopen(string hostname, int port, int [errno], string [errstr], int [timeout]);パラメータの意味を説明する必要はないと思います。はい、ここでは SMTP プロトコルが使用されているため、ポート番号は 25 です。接続が正常に開かれると、ファイル ハンドルのように使用できるソケット ハンドルが返されます。利用可能な操作には、fputs()、fgets()、feof()、fclose()
などが含まれます。
非常に簡単な紹介は以上です。
SMTPの基本
TCP/IPをベースとしたインターネットプロトコルの一般的なコマンド形式は、リクエスト/レスポンス方式で実装されており、すべてテキスト情報が使用されるため、
処理が容易です。 SMTP は Simple Mail Transfer Protocol の略称で、クライアントがサーバーに電子メールを送信できるようにします。したがって、
以下で説明するコマンドは、クライアントがサーバーにリクエスト命令を送信することを指し、応答はサーバーからクライアントに返される情報を指します。
SMTP は、コマンド ヘッダーとメッセージ本文の 2 つの部分に分かれています。コマンドヘッダーは主にクライアントとサーバー間の接続、検証などを完了します。プロセス全体は複数のコマンド
で構成されます。各コマンドがサーバーに送信されると、サーバーは応答情報 (通常は 3 桁の応答コードと応答テキスト) を返します。さまざまなサーバーから返される応答コードはプロトコルに準拠していますが、応答テキストは準拠していません。各コマンドと応答の最後にはキャリッジ リターン文字があるため、コマンドと応答は fputs() と fgets() を使用して処理できます。 SMTP コマンドと応答メッセージは 1 行です。メッセージ本文はメール
の本文部分であり、最後の終了行は終了行として独立した「.」である必要があります。
クライアントで一般的に使用される SMTP コマンドは次のとおりです:
HELO ホスト名: サーバーに挨拶し、使用されるマシン名をクライアントに通知します。任意の
MAIL FROM: sender_id: サーバーに送信者のアドレスを伝えます。 RCPT TO:receiver_id:サーバーの受信者のアドレスを伝えます
DATA:レターの内容は以下で送信され始め、それはのみを含む特別な行で終わる必要があります。
RESET:前のコマンドをキャンセルして最初から開始します
VERIFY userid: アカウントが存在するかどうかを確認します (このコマンドはオプションです)。サーバーがサポートしていない可能性があります)
QUIT: 接続を終了し、終了します
サーバーから返される応答情報は (形式: 応答コード + スペース + 説明) です):
220 サービス準備完了 (ソケット接続が成功するとこの情報が返されます)
221 処理中です
250 リクエスト電子メールアクションは正しく完了しています (この情報は、HELO、MAIL FROM、RCPT TO が成功した場合に返されます) 、QUIT コマンドが正常に実行されました)
354 データの送信を開始し、で終了します (DATA コマンドが正常に実行された場合、この情報が返されます。顧客端末は情報を送信するはずです)
500 構文エラー、コマンドが認識できません
550コマンドを実行できません。メールボックスが無効です
552 割り込み処理: ユーザーがファイル領域を超えています
簡単なコマンド ヘッダーを以下に示します (これはソケットを開いた後に行われます)。これは、stmp に電子メールを送信したテスト結果です。 .263.net:
HELO limodou
250 smtp.263.net
MAIL FROM: chatme@263.net
250 Ok
RCPT TO: chatme@263.net
250 Ok
DATA
354 データを
To で終了します。 : chatme@263.net
From: chatme@263.net
Subject: test
From: chatme@263.net
test
QUIT
250 OK: C46411C5097E0 としてキューに登録されました
これは SMTP の簡単な知識です。関連コンテンツは RFC にあります。
RFC 821 では、電子メールの送受信に関連するコマンドが定義されています。
RFC 822 は電子メールのコンテンツの形式を指定します。
RFC 2045-2048 はマルチメディア電子メール コンテンツの形式を策定し、
RFC 1113、1422-1424 は電子メールの機密性を向上させる方法について説明します。
send_mailクラスの実装
それでは私が書いたsend mailクラスを紹介していきます。上記の準備知識を踏まえて、以下のように実装していきます。
クラスのメンバー変数
var $lastmessage; //返された最後の応答メッセージを記録します
var $lastact; //ユーザーを歓迎するために HELO の後に使用されます
var $debug; //デバッグ情報を表示するかどうか
var $smtp; //smtpサーバー
var $port; //ソケットハンドル
このうち、$lastmessageと$lastactは最後の A 応答メッセージと実行されたコマンドを記録するために使用され、エラーが発生したときにユーザーが使用できます
。テストの必要があるため、$debug 変数も定義しました。その値が true の場合、実行中のプロセス中にいくつかの実行情報が表示されます。それ以外の場合は、出力はありません。 $fp は、開いたソケット ハンドルを保存するために使用されます。
クラスの構築
------------------------------------------ ------ -------------------------------------
関数 send_mail($smtp, $welcome="" , $debug=false)
{
if(empty($smtp)) die("SMTP を NULL にすることはできません!");
$this->smtp=$smtp;ようこそ))
{
$this->welcome=gethostbyaddr("localhost");
else
$this->welcome=$welcome;
$this- >lastmessage=" ";
$this->lafact=""
}
----------------- ----- -------------------------------------- ----- --------
このコンストラクターは主にいくつかの初期値の決定と設定を完了します。 $welcome は、サーバーにユーザーの名前を伝えるために HELO ディレクティブで使用されます。
HELO コマンドにはマシン名が必要ですが、マシン名なしで使用できます。ユーザーが $welcome を指定しない場合、ローカル マシン名が自動的に検索されます。
デバッグ情報を表示
------------------------------------------ -- -------------------------------------
1 関数 show_debug($message, $inout)
2 {
3 if ($this->debug)
4 {
5 if($inout=="in") //応答情報
6 {
7 $m='<< '; }
9 else
10 $m='>> ';
11 if(!ereg("n$", $message))
12 $message .= "
"; nl2br( $message);
14 echo "${m}${message}";
15 }
16 }
---------- -------------------------------------------------- -- ------------------
この関数はデバッグ情報を表示するために使用されます。 $inout には、アップロード命令であるか返される応答であるかを指定できます。アップロード命令である場合は "out" を使用し、返される応答である場合は "in" を使用します。
3行目はデバッグ情報を出力するかどうかを決定します。
5 行目で、応答メッセージであるかどうかを判断します。応答メッセージである場合は、7 行目の情報の前に「<<」を追加します。そうでない場合は、10 行目に「>>」を追加します。アップロード手順を区別します。
11-12行目、最後の情報文字列が改行文字かどうかを判断し、そうでない場合はHTML改行マークを追加します。 13 行目は、すべての改行文字を HTML
改行タグに変換します。
14 行目、メッセージ全体を出力し、違いを示すためにメッセージの色をグレーに設定します。
コマンドを実行
------------------------------------------ ------ -------------------------------------
1 関数 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;
12 else
14 }
----- ----- -------------------------------------- ----- --------------------
ソケット処理部分では、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 {
3
4 //接服务器
5 $this->lastact="connect";
6
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 return false;
19 }
20 else
21 {
22 $this->lastact="HELO " . $this->ようこそ 。 「ん」;
23 if(!$this->do_command($this->lastact, "250"))
24 {
25 fclose($this->fp);
26 false を返します。
27 }
28
29 $this->lastact="MAIL FROM: $from" 。 「ん」;
30 if(!$this->do_command($this->lastact, "250"))
31 {
32 fclose($this->fp);
33 false を返します。
34 }
35
36 $this->lastact="RCPT TO: $to" 。 「ん」;
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_debug("接続に失敗しました!", "in");
90 false を返します。
91 }
92 }
------------------------------------------ -------------------------------------
いくつかの意思決定は明確です。
この関数は 4 つのパラメータを共有し、それぞれ $to 受信者表示、$from 送信者表示、$subject 表要求項目主および $message
処理が成功した場合は true を返し、失敗した場合は false を返します。
8行目、成功コードが220の場合、ソフトウェアサーバに接続する。
12行目、戻すべき情報が継続できないことを示すブロックモードを設定する。詳細に説明する。
29〜34行目、MAIL FROM命令を処理し、コードが250であることが期待される。 -41行目、RCPT TO命令を処理し、コードは250であることが期待される。
44-49行目、DATA命令を処理し、コードは354であることが期待される。 56行目、$subjectが空でない場合、パッケージ本体に主セクションがあるかどうかを検査し、ない場合は主セクションを追加する。
66〜70回目は、$toが空でない場合、ファイルボディに信人部分があるかどうかを検査し、存在しない場合は、信人部分を追加します。 73〜74行目では、追加のコンポーネントの終了行があるかどうかを検査し、追加のコンポーネントがない場合は、個別の行として「.」が送信されます。
78〜83行目、サーバーとの接続に応じてQUITを実行し、コードは250であることが期待される。
85行目、処理成功マーク(true)を返す。これは、send_mail タイプ全体の実現にとってはそれほど困難ではありません。
メール送信例
まず最も単純な例を示します:
------------------------------------ --- ----------------------------------------
1 include "sendmail.class.php3";
2 $email="こんにちは、これはテストレターです!";
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 "送信成功 ! < ;br>";
7 }
8 else
9 {
10 echo "送信に失敗しました!
";
11 }
?>
------------- - ------------------------------------------------- - -----------------
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' //受信者のメールアドレスに変更しました。 address
$ str = "ニュースレター ".date('MY', time());
//情報は私によって変更されました
$html_data = '