NET // communication networks
LESSON 28 / 発展編

ネットワークプログラミングとサーバの実体

標準編で telnet を使って観察した HTTP / SMTP / POP3 / FTP の応答 ─ その正体に踏み込む発展回。サーバクライアントも神秘的な箱ではなく、OS 上で動くただのプロセスです。サーバは TCP/UDP のポートを listen し、クライアントはそこへ connect する。ps / ss / lsof で実体を見て、Python で最小限のクライアント/サーバを動かし、Apache・nginx・BIND・Postfix などの daemon と同じ対応関係で理解します。本講のあと 第29回 HTTP プログラミング で「自分で HTTP を書く」 側に進み、続く 第30回 Wireshark でパケットを観る で「自分が書いた通信がワイヤをどう流れるか」を観察します。

学習目標

本講を終えると、以下を達成できるようになります。

CORE MESSAGE サーバ = ポートを listen するプロセスクライアント = サーバのポートに connect するプロセス。Apache でも nginx でも Python でも、HTTP を話して 80 / 8000 番を listen していれば、ネットワークから見れば「Web サーバ」です。

このレッスンの目次

01 なぜプロセスか 第16回で見た 220 や 200 OK を、誰が返していたのかを明確にします。 02 実際に見る ps・ss・lsof で、PID と listen ポートの対応を確認します。 03 クライアントは connect するプロセス socket での生 HTTP リクエストで「クライアント=ポートに接続するプロセス」を体感します。 04 サーバは listen するプロセス python -m http.server で「サーバ=ポートを掴むプロセス」を実感します。 05 代表的サーバ Web・DNS・メール・FTP の daemon と既定ポートを整理します。 06 三角関係 プロセス・ポート・プロトコルを切り分けます。 07 停止・再起動 systemctl、reload、reverse proxy、複数プロセスを見ます。 08 まとめと用語 重要語句と daemon 一覧を確認します。 09 確認問題 5 問で理解度をチェックします。

なぜ「プロセス」を強調するのか

標準編 第16回 telnet で覗くアプリ層プロトコル では、telnet で HTTP の 200 OK、SMTP / FTP の 220、POP3 の +OK を見ました。では、その文字列を送り返している主体は何でしょうか。答えは OS 上で実行中の プロセス です。

Web サーバとは「TCP 80 を listen して HTTP メッセージを処理するプロセス」のことです。実装は Apache HTTP Server でも nginx でも、Python の http.server でも、Go の net/http でもかまいません。HTTP というプロトコルに従って要求を読み、応答を書き返せば、ネットワーク上の相手からは Web サーバとして見えます。

POINT 「サーバ」は物理マシン名ではなく、正確には 特定のポートを listen しているプロセス を指す。1 台のマシン上に、HTTP・SMTP・DNS・SSH など複数のサーバプロセスが同時に存在できる。

1 台のサーバマシンの中に複数のプロセスがいる

PC サーバマシン 203.0.113.10 browser PID 4312 connect :80 httpd / nginx PID 1280 / TCP :80 / HTTP smtpd PID 1302 / TCP :25, :587 / SMTP named PID 1320 / UDP,TCP :53 / DNS sshd / dovecot / vsftpd ... PID 1350... / :22, :110, :143, :21 TCP 接続 宛先 IP + 宛先ポートで到達

図の見方:左のブラウザもプロセス、右の httpd / smtpd / named もプロセスです。サーバマシンが偉いのではなく、どの PID のプロセスがどのポートを listen しているか が実体です。

つながる知識: 標準編で telnet が接続していた相手は「HTTP という概念」ではなく、その瞬間に 80 番ポートを掴んでいた実行中プロセスです。だから nginx を止めれば 80 番の応答は消え、Python サーバを 8000 番で起動すれば 8000 番に新しい応答が現れます。

確認: 「サーバが応答する」という現象の本質として、最も適切なものはどれか。

正解:C。サーバとは「特定のポートで待ち受ける実行中プロセス」の役割名。プロトコルは概念、ポート番号は単なる 16 bit の識別子で、実体は「今そのポートを掴んでいる PID」。だから systemctl stop nginx すれば 80 番の応答は消える。

プロセスとポートを実際に見る

サーバの実体を確認するには、プロセス一覧listen ポート一覧 を見ます。Linux なら psss、macOS なら lsof が手軽です。

常駐プロセスを探す

# Linux / macOS
ps -ef | grep -E 'httpd|nginx|named|postfix|dovecot|vsftpd' | grep -v grep

# BSD/macOS 風の表示が好みなら
ps aux | grep -E 'httpd|nginx|named|postfix|dovecot|vsftpd' | grep -v grep

listen しているポートを探す

# Linux: TCP の listen 中ソケットとプロセス
sudo ss -tlnp

# Linux: UDP も含めて見る
sudo ss -tulnp

# macOS: listen 中のプロセスを一覧
sudo lsof -i -P -n | grep LISTEN

# 特定ポートだけ見る
sudo lsof -i :80 -P -n
オプションの読み方: ss -tlnpt=TCP、l=LISTEN、n=名前解決せず数字で表示、p=プロセス表示。lsof -i -P -n はインターネットソケットを、ポート番号と IP を数字のまま表示します。

walkthrough: nginx を起動して止める

  1. nginx を起動する
sudo systemctl start nginx
ps aux | grep nginx | grep -v grep
  1. :80 を nginx が掴んでいることを確認する
sudo ss -tlnp | grep ':80'

# 出力例
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
  1. HTTP で応答を確認する
curl -I http://localhost/

# 出力例
HTTP/1.1 200 OK
Server: nginx
  1. nginx を止めると :80 が消える
sudo systemctl stop nginx
sudo ss -tlnp | grep ':80'   # 何も出なければ listen していない
EADDRINUSE 同じ IP アドレス・同じプロトコル・同じポートを、通常は 2 つのプロセスが同時に listen できません。すでに nginx が 0.0.0.0:80 を掴んでいる状態で Apache を 80 番に起動しようとすると、典型的には Address already in use / EADDRINUSE で失敗します。

Q. 自分の PC で nginx を 80 番で起動し、別ターミナルで ss -tlnp 'sport = :80' を実行すると何が見えるか? その後 sudo systemctl stop nginx してから同じコマンドを実行すると、表示はどう変わるか?

クライアントは「ポートへ connect するプロセス」

クライアントとは「相手のポートへ接続し、要求を書き、応答を読むプロセス」です。本講では 「クライアントもプロセスである」 ことを実感する最小例だけを示し、HTTP の細かい書き方(http.client / requests / aiohttp / Node.js / 認証実装) は 第29回 HTTP プログラミング で扱います。

最小例:socket で生 HTTP リクエストを送る

抽象度を最も下げた例として、TCP ソケットを自分で作り、HTTP/1.1 のリクエスト行とヘッダを文字列で送ります。「接続=ソケット、応答=バイト列」という事実をまず体感する目的のコードです。

# raw_http_client.py
import socket

host = "example.com"
request = (
    "GET / HTTP/1.1\r\n"
    f"Host: {host}\r\n"
    "Connection: close\r\n"
    "\r\n"
)

with socket.create_connection((host, 80), timeout=5) as s:
    s.sendall(request.encode("ascii"))
    chunks = []
    while True:
        data = s.recv(4096)
        if not data:
            break
        chunks.append(data)

print(b"".join(chunks).decode("utf-8", errors="replace")[:1000])
LOWER LAYER 上の socket 版も、urllibrequests といった上位ライブラリも、内部では TCP 80 へ socket している という点は同じです。ライブラリは便利な皮であり、下層の実体はソケット・ポート・プロセス間通信です。
もっと書きたい人へ: 第29回 HTTP プログラミング では http.client / requests / aiohttp(非同期) / Node.js fetch での書き方、Cookie・セッション・Bearer 認証の実装、curlmitmproxy によるデバッグ手段までを横並びで扱います。

確認: クライアントが TCP で connect() するとき、自分側のポート番号(送信元ポート)はどう決まるか?

正解:B。クライアントが bind() せずに connect() すると、OS が空いている短命ポート(エフェメラルポート、Linux は通常 32768〜60999)から自動で割り当てる。これにより同じクライアントから同じサーバへ複数同時接続しても、4-tuple(送信元IP・送信元ポート・宛先IP・宛先ポート)が異なるので別接続として扱える。

サーバは「ポートを bind / listen するプロセス」

サーバとは「自分のポートを bind し、listen し、接続を受け、プロトコルに沿って応答するプロセス」です。本講では「プロセスがポートを掴んで応答を返す」事実を実感する最小例だけを示し、動的応答や JSON / WebSocket / 認証の実装第29回 HTTP プログラミング に譲ります。

1 行でディレクトリを公開する

# カレントディレクトリを http://localhost:8000/ で公開
python3 -m http.server 8000

# 別ターミナルから確認 ─ 「PID + listen ポート」が見える
curl -I http://localhost:8000/
lsof -i :8000 -P -n

ポイントは lsof -i :8000 の出力に 「python3 (PID) ─ TCP *:8000 (LISTEN)」 が現れること。これだけで 「Python プロセスが Web サーバになっている」 という事実が直接見えます。

動的応答や複雑なエンドポイントを書きたい人へ: BaseHTTPRequestHandler での do_GET 実装、JSON 応答、Flask / FastAPI、Node.js の http モジュール、Cookie/セッション、Bearer 認証などは 第29回 HTTP プログラミング でまとめて扱います。本講ではあくまで「サーバ = ポートを掴む 1 つのプロセス」という骨格の確認に留めます。

telnet で自作サーバを叩く

標準編 第16回 telnet の手法で、自作サーバの応答も観察できます。

python3 -m http.server 8000 &

# 別ターミナル
telnet 127.0.0.1 8000
GET / HTTP/1.1
Host: localhost
Connection: close

# 空行まで送ると、HTTP/1.0 200 OK などの応答が返る
クライアント側 サーバ側 browser / curl / telnet connect 127.0.0.1:8000 python3 mini_server.py PID 7742 / TCP 127.0.0.1:8000 HTTP を話すプロセス GET / HTTP/1.1 200 OK + body

図の見方:Python プログラムも Apache も、ネットワークから見ると「HTTP を話すプロセス」です。違いは実装・性能・機能の厚みであって、ポートで待ち受けてリクエストに応答する という骨格は同じです。

ローカルで試すなら 127.0.0.1 に bind すると自分の PC からだけ接続できます。外部から接続させたい場合は 0.0.0.0 に bind しますが、ファイアウォールや公開範囲に注意してください。学習用の簡易サーバをインターネットへ露出させる必要はありません。

Q. 同一 PC で Apache と nginx の両方を 80 番で同時起動しようとすると何が起きるか? 一方を 8080 番に変更すれば共存できるか? その場合、利用者から見て何が変わるか?

代表的なサーバプログラム

サーバプログラムは、OS の裏側で常駐して要求を待つことが多いため daemon と呼ばれます。名前は違っても、見るべき軸は「役割」「話すプロトコル」「プロセス名」「既定ポート」です。
サーバ役割主役プロトコルプロセス名既定ポート公式 URL
Apache HTTP Server古典的・モジュラな Web サーバHTTP / HTTPShttpd / apache280 / 443httpd.apache.org
nginx高速・軽量な Web サーバ。リバースプロキシにも多用HTTP / HTTPSnginx80 / 443nginx.org
BIND権威 DNS / キャッシュ DNS の双方を担える DNS サーバDNSnamedUDP/TCP 53isc.org/bind
PostfixMTA。メールを受け取り、配送するSMTP / Submissionmaster / smtpd25 / 587postfix.org
DovecotMDA / IMAP・POP3 サーバ。ユーザがメールを取り出すIMAP / POP3dovecot / imap-login143 / 110 / 993 / 995dovecot.org
vsftpd / pure-ftpdFTP サーバ。ファイル転送の制御接続とデータ接続を扱うFTPvsftpd / pure-ftpd21 / 20vsftpd / pure-ftpd

最短で動かすレシピ(Linux)

対象起動挙動確認
Apachesudo apt install apache2 && sudo systemctl start apache2curl -I http://localhost/
nginxsudo apt install nginx && sudo systemctl start nginxcurl -I http://localhost/
BINDsudo apt install bind9 && sudo systemctl start bind9dig @localhost example.com
Postfixsudo apt install postfixtelnet localhost 25220
Dovecotsudo apt install dovecot-pop3d dovecot-imapdtelnet localhost 110+OK
vsftpdsudo apt install vsftpd && sudo systemctl start vsftpdftp localhost または telnet localhost 21
macOS では Homebrew で brew install nginx bind postfix dovecot vsftpd のように導入できます。ただしサービス管理は systemctl ではなく brew services です。Windows では WSL か Docker を使うと、Linux とほぼ同じコマンドで実験できます。
IMPLEMENTATION 「HTTP サーバ」は Apache という製品名ではありません。HTTP を理解し、ポートで待ち受け、要求に応答するプロセス の役割名です。Apache / nginx / Python / Go は、その役割を実装する具体物です。
もっと詳しく:nginx と Apache の住み分けが起きた理由

1995 年生まれの Apache は 「リクエストごとにプロセス/スレッドを生成」 するモデル(prefork)で、CGI や PHP などのレガシー資産との親和性が高い。一方 2004 年生まれの nginx は 「1 プロセスでイベント駆動 I/O 多重化」 し、何万もの同時接続を 1 ワーカで捌ける設計。

同じ「HTTP サーバ」役でも、内部実装(プロセスモデル・I/O モデル)が違うので得意分野が分かれる。役割は同じ、実装の選択肢として複数ある、という見方。

プロセス・ポート・プロトコルの三角関係

ネットワークの会話を読むときは、プロセスポートプロトコル を混同しないことが重要です。この 3 つは近い関係にありますが、同じものではありません。
実行中のプロセス 例: nginx / httpd / python3 PID OS が実行中プロセスに割り当てる番号 例: pid=1234 listen ポート 接続を待つ入口 例: TCP :80 / UDP :53 話すプロトコル メッセージの約束 HTTP / SMTP / DNS / FTP ... 実装プログラム 同じ機能でも複数実装 Apache / nginx / Python

図の見方:PID は OS の管理番号、ポートはネットワーク上の入口、プロトコルは会話の文法、実装プログラムは具体的なコードです。同じ HTTP でも実装プロセスは様々で、同じプロセスが複数ポートを listen することもあります。

同じ機能・違う実装

Apache、nginx、Python の http.server はすべて HTTP 応答を返せます。機能・性能・設定方法は違いますが、HTTP を話すという意味では同じ役割です。

同じプロセス・複数ポート

Dovecot は IMAP 143、IMAPS 993、POP3 110、POP3S 995 のように複数の入口を持てます。1 プロセス群が複数プロトコル・複数ポートを扱う構成もあります。

同じポート・同時に1つ

同じ IP・同じ TCP/UDP・同じポートは、通常 1 つのプロセスだけが bind できます。これがポート競合です。

確認: 「ポート 443 で動いているなら必ず HTTPS(TLS over TCP)である」という認識は正しいか?

正解:C。実際に HTTP/3(QUIC)は 443/UDP を使うし、gRPC やカスタムプロトコルを 443 で動かす運用もある。ポート番号と上位プロトコルの対応は 慣習(/etc/services など)で IANA が登録番号を管理しているだけ。OS は「このポートはこのプロトコル専用」とは強制しない。だから telnet で 443 に繋いで生バイトを送ると(TLS でないため)サーバ側でエラー応答になる。

プロセスは「停止」「再起動」「複数同時」がある

実体がプロセスである以上、サーバは CPU とメモリを消費し、ログを書き、設定を読み込み、予期せず停止することがあります。常駐サーバを運用するときは、プロセスの状態を見る操作が必要です。

systemctl で状態を見る

# 稼働状態を見る
systemctl status apache2

# 設定を再読み込みする。接続中の処理をできるだけ保つ
sudo systemctl reload apache2

# プロセスを止めて起動し直す
sudo systemctl restart apache2

# ログを見る
journalctl -u apache2 -f
reload と restart: reload は設定再読み込み、restart は停止してから起動です。設定ファイルを変えた後にどちらを使うべきかは daemon によります。Web サーバでは、既存接続への影響を小さくするため reload を使う場面が多いです。

複数プロセスとリバースプロキシ

client browser nginx PID 2001 listen TCP :80 / :443 リバースプロキシ backend Apache apache2 PID 3101 :8081 apache2 PID 3102 :8082 apache2 PID 3103 :8083 HTTP

図の見方:クライアントから見える入口は nginx の 80 / 443 番だけでも、その裏で複数の Apache プロセスやアプリサーバが動く構成があります。ロードバランサやリバースプロキシも、結局は 別のポートへ接続するプロセス です。

REALITY サーバの実体はプロセスです。だから 停止することがある再起動する複数起動するCPU/メモリを消費するログを残す。ネットワーク学習で「サーバ」という言葉が出たら、頭の中で PID と listen ポートを持つ実行中プログラム に置き換えると、挙動が具体的に見えます。

確認: 「同じプログラムが複数のプロセスとして同時起動できる」例として、ポート競合を起こさず確実に成立するのはどれか。

正解:A。ポートが違えば同じプログラムでも別プロセスとして共存可能(リバースプロキシで内側に複数の HTTP サーバを動かす構成は実運用でよくある)。B・C は同じポート競合で後発が EADDRINUSE 失敗。D は誤り(プロセスは独立した実行単位なので、リソースが許せば同じバイナリの複数起動は普通に可能)。

まとめと用語チェック

SUMMARY 1. サーバは「ポートを listen して要求を待つプロセス」、クライアントは「そのポートへ connect するプロセス」
2. 第16回で telnet に応答していた正体は、HTTP / SMTP / POP3 / FTP を話す各 daemon プロセス
3. ps でプロセス、ss / lsof で listen ポートを見ると、PID とポートの対応が確認できる
4. Python の socket / urllib / requests は抽象度が違うだけで、下層ではソケットで TCP 接続している
5. Apache / nginx / Python はどれも HTTP を話せる実装。役割名と製品名を混同しない
6. 同じポートを同時に 2 つのプロセスで listen しようとすると、通常は EADDRINUSE で失敗する
7. daemon は停止することがあり、再起動し、ログを残す。神秘的な存在ではなく OS 上の実行中プログラムである

重要語句

用語1 行説明
プロセスOS 上で実行中のプログラム。PID を持ち、CPU/メモリなどの資源を使う
listenサーバ側プロセスがポートで接続待ちする状態
bindソケットにローカル IP アドレスとポート番号を結び付ける操作
socketアプリがネットワークと読み書きする窓口。IP アドレス・ポート・プロトコルと関係する
daemonバックグラウンドで常駐し、要求を待つサーバプロセス
MTAMail Transfer Agent。メールを受け取り、配送する役割。Postfix など
MDAMail Delivery / Retrieval 側の役割として、ユーザがメールを取り出せるようにする。Dovecot など
リバースプロキシクライアントからの入口になり、裏側の別サーバへ要求を転送するサーバプロセス。nginx が代表例

代表 daemon の主役プロトコル

daemon主役プロトコル典型ポート
httpd / apache2HTTP / HTTPS80 / 443
nginxHTTP / HTTPS80 / 443
namedDNS53(UDP/TCP)
master / smtpd(Postfix)SMTP / Submission25 / 587
dovecot / imap-loginIMAP / POP3143 / 110 / 993 / 995
vsftpd / pure-ftpdFTP21 / 20
NEXT: 次回 第29回 HTTP プログラミング では、本講で固めた「サーバ・クライアントは1つのプロセス」という見方を踏まえて、HTTP を実際に書く側へ進みます。curl / Python (http.client / requests / aiohttp) / Node.js fetch / 自作最小サーバ / Cookie・Bearer 認証 / mitmproxy デバッグ までを横並びで扱います。
本講は標準編 第16回 telnet で覗くアプリ層プロトコル の続きにあたる発展回です。telnet で見えていた応答の「正体」が、本講の整理で1つの絵に収まるはずです。

確認問題

問1. ポート 80 を listen するために本質的に必要なものとして、最も適切なものを 1 つ選べ。

次の選択肢から最も適切なものを選択してください。
A. Apache HTTP Server そのもの。Apache 以外は Web サーバになれない
B. TCP 80 を bind/listen し、HTTP 要求に応答するプロセス
C. 物理的にサーバ専用機として販売されているコンピュータ
D. DNS に登録されたドメイン名
正解:B
Apache は代表的な実装ですが、本質は TCP 80 を listen して HTTP を話すプロセス です。nginx や自作プログラムでも同じ役割を担えます。

問2. ss -tlnp の出力に users:(("nginx",pid=1234,fd=6)) とある。意味として最も適切なものを選べ。

次の選択肢から最も適切なものを選択してください。
A. nginx という DNS 名が pid=1234 の IP アドレスに解決された
B. nginx がクライアントとして外部の 1234 番ポートへ接続した
C. nginx プロセス(PID 1234)が、その listen ソケットを fd=6 として保持している
D. nginx の設定ファイルが 6 行目でエラーになっている
正解:C
users:(("nginx",pid=1234,fd=6)) は、そのソケットを nginx プロセスがファイルディスクリプタ 6 として持っている、という意味です。

問3. python3 -m http.server 8000 を実行すると何が起きるか。

次の選択肢から最も適切なものを選択してください。
A. Python プロセスが TCP 8000 番を listen し、カレントディレクトリを HTTP で配信する
B. OS のカーネルが自動的に Apache をインストールする
C. UDP 8000 番で DNS サーバが起動する
D. ブラウザが 8000 個同時に起動する
正解:A
Python 標準ライブラリの HTTP サーバが起動し、現在のディレクトリを配信します。学習用・簡易確認用であり、本番公開用の高機能サーバではありません。

問4. Postfix と Dovecot の役割の違いとして、最も適切なものを選べ。

次の選択肢から最も適切なものを選択してください。
A. Postfix は Web サーバ、Dovecot は DNS サーバである
B. Postfix は IMAP 専用、Dovecot は SMTP 専用である
C. どちらも FTP のデータコネクションだけを処理する
D. Postfix は SMTP でメールを受け取り配送する MTA、Dovecot は IMAP/POP3 でユーザにメールを取り出させるサーバである
正解:D
Postfix はメール配送側、Dovecot はユーザがメールボックスから読む側です。第16回で見た SMTP と POP3 / IMAP の違いが、そのまま daemon の役割の違いになります。

問5. 同じ IP アドレスの同じ TCP ポートを 2 つのプロセスで同時に listen しようとすると、通常どうなるか。

次の選択肢から最も適切なものを選択してください。
A. OS が自動的に 2 つのプロセスへ完全に同じデータを複製して配る
B. 後から bind したプロセスが Address already in use / EADDRINUSE で失敗することが多い
C. ポート番号が 80 から 81 に自動変更され、必ず成功する
D. DNS サーバだけが例外なく停止する
正解:B
同じ IP・同じプロトコル・同じポートは、通常 1 つのプロセスだけが bind/listen できます。すでに使われている場合は EADDRINUSE になります。
← PREV
第27回 ファイアウォール・IDS/IPS
NEXT →
第29回 HTTP プログラミング