概要

SMTP(Simple Mail Transfer Protocol)はメール送信の基盤となるプロトコルです。実務では Jakarta Mail や Spring Mail を使うため直接触る機会は少ないですが、メール送信のトラブルシューティングでは SMTP コマンドの理解が欠かせません。認証エラーの原因がクレデンシャルなのか TLS 設定なのか、SMTP のレベルで切り分けられるかどうかが解決速度を左右します。この記事では、TCP ソケットを使って SMTP コマンドを手作りし、EHLO による接続確立、AUTH LOGIN による認証、MAIL FROM / RCPT TO / DATA によるメール送信の一連のフローを確認します。実務での送信は Jakarta Mail を使うべきですが、プロトコルの構造を理解しておくことで障害対応の幅が広がります。

使いどころ

メール送信が失敗した際に、SMTP レベルのコマンドを手動実行して原因を切り分ける

SMTP サーバーの EHLO レスポンスを確認し、対応する認証方式や拡張機能を調べる

メール配信システムの監視ツールで、SMTP の疎通確認を行うヘルスチェック機能を実装する

コード例

SMTP プロトコルの基本フローをソケットで確認する
import java.io.*;

public class SmtpSocketSample {

    record SmtpStep(String commandLabel, String clientLine,
                    String serverResponse) {}

    static void showSmtpFlow() {
        String overview = """
                SMTP(Simple Mail Transfer Protocol)は
                メール送信に使われるテキストベースのプロトコルです。
                クライアントとサーバーがコマンドと応答コードを
                やり取りしてメールを転送します。
                """;
        System.out.println(overview);

        var steps = List.of(
            new SmtpStep("接続", "(TCP 接続)",
                "220 smtp.example.com ESMTP ready"),
            new SmtpStep("あいさつ", "EHLO client.example.com",
                "250 AUTH LOGIN PLAIN"),
            new SmtpStep("認証開始", "AUTH LOGIN", "334 Username:"),
            new SmtpStep("ユーザー名",
                Base64.getEncoder().encodeToString(
                    "[email protected]".getBytes()),
                "334 Password:"),
            new SmtpStep("送信者指定", "MAIL FROM:<[email protected]>",
                "250 OK"),
            new SmtpStep("受信者指定", "RCPT TO:<[email protected]>",
                "250 OK"),
            new SmtpStep("本文開始", "DATA",
                "354 Start mail input"),
            new SmtpStep("本文終了", ".", "250 Message accepted"),
            new SmtpStep("切断", "QUIT", "221 Bye")
        );

        System.out.println("=== SMTP コマンドの流れ ===");
        for (var step : steps) {
            System.out.printf("%-10s  C: %-40s  S: %s%n",
                "[" + step.commandLabel() + "]",
                step.clientLine(),
                step.serverResponse());
        }
    }

    public static void main(String[] args) {
        showSmtpFlow();
        System.out.println();
        System.out.println("注意: 実務では Jakarta Mail を使用してください");
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

record で SMTP ステップを構造化し、テキストブロックでプロトコルの説明文を読みやすく記述できる。List.of で不変リストを活用。

Java 17
// Java 17: record + テキストブロック
record SmtpStep(String label,
    String clientLine, String serverResponse) {}
var steps = List.of(
    new SmtpStep("接続", "(TCP 接続)",
        "220 smtp.example.com ESMTP ready"),
    new SmtpStep("あいさつ",
        "EHLO client.example.com",
        "250 AUTH LOGIN PLAIN"));
String overview = """
    SMTP は メール送信に使われる
    テキストベースのプロトコルです。
    """;

Library Comparison

標準 API(Socket + 手動コマンド送信)SMTP プロトコルの学習や障害切り分けツールの実装。TLS ハンドシェイク、MIME エンコーディング、添付ファイルなど全て自前で実装する必要がある。
Jakarta Mail(旧 JavaMail)実務でのメール送信。SMTP の接続・認証・MIME 組み立てを一括で扱える。外部依存が追加される。プロトコルの詳細は隠蔽されるため学習には向かない。
Spring Mail(JavaMailSender)Spring Boot プロジェクトでの DI ベースのメール送信。Spring 依存が前提。SMTP の仕組み理解には役立たない。

注意点

この記事のコードは SMTP プロトコルの仕組み理解が目的。実務でのメール送信には Jakarta Mail を使うこと

AUTH LOGIN の認証情報は Base64 エンコードされるだけで暗号化されない。必ず TLS/STARTTLS と組み合わせること

SMTP サーバーのレスポンスコードは 3 桁の数字で始まる。2xx は成功、3xx は追加情報要求、4xx/5xx はエラーを表す

Gmail などのサービスではアプリパスワードの発行が必要。通常のパスワードでは認証できない

FAQ

EHLO と HELO の違いは何ですか。

EHLO は拡張 SMTP(ESMTP)のあいさつコマンドで、サーバーが対応する拡張機能(認証方式など)を返します。HELO は旧仕様で機能拡張の情報が返りません。現在は EHLO を使うのが標準です。

STARTTLS と SSL/TLS の違いは何ですか。

STARTTLS はポート 587 で平文接続を開始し、途中で TLS に昇格します。SSL/TLS はポート 465 で最初から暗号化接続です。現在は STARTTLS(ポート 587)が推奨されています。

このコードで実際にメールを送信できますか。

プロトコルの理解用のデモコードです。実際のメール送信には TLS 接続や MIME エンコーディングが必要で、Jakarta Mail の使用を推奨します。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます