---
title: "ソケット通信とは？TCP/UDPソケットの仕組み・API手順とOSで違う落とし穴を実測で解説"
url: "https://www.issoh.co.jp/tech/details/14680/"
published: 2026-07-14
updated: 2026-10-17
categories: ["プロトコル"]
publisher: "株式会社一創"
---

# ソケット通信とは？TCP/UDPソケットの仕組み・API手順とOSで違う落とし穴を実測で解説

ソケット通信とは、IPアドレスとポート番号で決まる「通信の端点」をOSに作らせ、その端点どうしでデータをやり取りする仕組みです。Webサーバーもデータベース接続もSSHも、下をたどるとソケットの上で動いています。この記事では、TCPソケットとUDPソケットの違い、socket・bind・listen・accept・connectという呼び出しの順番を押さえたうえで、LinuxとmacOSで実際に動かして分かった挙動の差（TIME\_WAITの長さ、「Address already in use」が消える条件、送信元ポートの範囲）までを扱います。実測は2026年10月17日に、Ubuntu 24.04（カーネル6.8・Python 3.12.15）のコンテナとmacOS 26.6.2（Python 3.9.6）で行いました。

## まとめ：ソケット通信の仕組みと実装前に押さえる要点

- ソケットは「IPアドレス＋ポート番号＋プロトコル」で決まる通信の端点で、LinuxやmacOSではファイルディスクリプタで扱い、WindowsのWinsockではSOCKETハンドルで扱います。
- **TCPソケットは境界の無いバイト列**です。3回に分けて送ったデータが、受信側では1回の`recv`でまとめて返りました。区切りはアプリケーションが決めます。UDPは1回の送信が1データグラムのまま届きます。
- サーバーは socket→bind→listen→accept、クライアントは socket→connect の順に呼びます。`accept`は待ち受けとは別の、接続ごとのソケットを返します。
- UDPでも`connect`を呼ぶと宛先が固定され、相手のポートが閉じているときに`ECONNREFUSED`を受け取れるようになります。
- TIME\_WAITの残り時間は**Linuxで約60秒、macOSで約30秒**でした。再起動直後の「Address already in use」は、**Linuxでは旧・新の両方のソケットにSO\_REUSEADDRが要り**、macOSでは新しい側だけで消えます。
- 長時間無通信の接続は、AWSのNAT Gateway（350秒）やALB（既定60秒）で切られます。NAT GatewayにはLinuxのキープアライブ既定（7200秒）では間に合わず、ALBはデータの送受信が無いと切れます。

以下、仕組み→APIの流れ→OSごとの違い→運用の順に説明します。

## ソケット通信の仕組み：IPアドレスとポート番号で決まる通信の端点

「ソケット」という語は二つの意味で使われます。一つは通信の端点そのもの、もう一つはその端点を操作するためにOSが用意した関数群（ソケットAPI）です。TCP/IPの階層でいえば、ソケットはアプリケーションとトランスポート層（TCP・UDP）の境目にある窓口で、パケットの分割や再送、チェックサムの計算はOSが受け持ちます。プログラムは「どこに接続するか」と「何バイト送るか」だけを指示します。階層の全体像は[TCP/IPの4階層モデル](/tech/details/13083/)で確認できます。

### ソケットはOSから渡されるファイルディスクリプタ

Linuxや macOSでは、`socket()`を呼ぶとOSが端点を作り、整数の番号（ファイルディスクリプタ）を返します。ファイルを開いたときと同じ番号の体系なので、`read`・`write`・`close`がそのまま使えます。後で示す実測では、待ち受け用のソケットが3番、`accept`が返した通信用のソケットが4番でした。プロセスのファイルディスクリプタ上限（`ulimit -n`）は、同時接続数を制約します。ただし標準入出力、ログファイル、待ち受けソケットなども枠を使うため、接続数の上限と同じ値にはなりません。

### 5つの値の組で接続を見分ける仕組み

端点の住所はIPアドレスとポート番号の組です。IPアドレスがホストを、ポート番号がそのホスト上のどのプログラムかを指します。TCPの接続は「送信元IP・送信元ポート・宛先IP・宛先ポート・プロトコル」の5つの値で区別されます。サーバーが443番だけで待ち受けていても、クライアントごとに送信元のIPとポートが違うので、何千本の接続でも混ざりません。クライアント側の送信元ポートはOSが自動で払い出します（実測ではLinuxで43698・43704・43718のように割り当てられました）。

## TCPソケットとUDPソケットの違いと使い分け

ソケットを作るときに、ストリーム型（`SOCK_STREAM`＝TCP）かデータグラム型（`SOCK_DGRAM`＝UDP）かを指定します。この選択で、届き方の保証とデータの区切り方が決まります。

| 観点      | TCPソケット        | UDPソケット            |
| ------- | -------------- | ------------------ |
| 接続      | 三ウェイハンドシェイクで確立 | 張らない               |
| 到達・順序   | 再送と順序番号で保証     | 保証しない              |
| データの区切り | 無い（バイト列）       | 1送信＝1データグラム        |
| 主な用途    | HTTP・DB接続・SSH  | DNS・音声/映像・ゲームの状態同期 |

### TCPソケットの接続確立とバイト列の境界

TCPは通信の前にSYN、SYN+ACK、ACKの3つのやり取りで接続を確立します（[RFC 9293](https://www.rfc-editor.org/rfc/rfc9293.html)の「three-way handshake」）。TCPは再送と順序制御によって信頼性のあるバイト列を提供しますが、通信障害による接続失敗は起こり得ます。また、送信の単位は保たれません。クライアントから`sendall`で `hello`・`world`・`!!` の3回に分けて送り、サーバーが0.3秒待ってから`recv(1024)`を呼ぶと、LinuxでもmacOSでも1回の`recv`で `b'helloworld!!'` がまとめて返りました。逆に、大きなデータが複数回の`recv`に分かれて届くこともあります。

つまり、TCPでメッセージをやり取りするなら、区切りをアプリケーションが決める必要があります。よく使うのは「先頭4バイトに本文の長さを入れる」方式と「改行で区切る」方式です。HTTP/1.1ではヘッダー行をCRLFで区切り、本文の境界は`Content-Length`やチャンク転送などで示します。`recv`が要求した長さより短く返る前提で受信ループを組む具体的なコードは、[Pythonで作って動かすソケットの実装入門](/tech/details/17511/)で扱っています。

### UDPソケットのデータグラム境界と配送の制約

同じ3回の送信をUDPで行うと、受信側の`recvfrom`は `b'hello'`、`b'world'`、`b'!!'` の3回に分かれて返りました。UDPはデータグラムの境界を保持するため、メッセージの区切りを別途設ける必要はありません。ただし受信バッファが小さいと、データグラムは切り詰められます。また、届かない、順番が入れ替わる、重複するといったことは起こり得ます。DNSのように1往復で済む問い合わせや、古いデータを再送しても意味がない音声・映像に向きます。データを取りこぼせない用途でUDPを選び、再送と順序制御を自作するのは、TCPを作り直すことになるので避けてください。

## ソケットAPIの流れ：サーバーはsocket→bind→listen→accept、クライアントはsocket→connect

ここではTCPの呼び出し順を示します。C言語でもPythonでも基本的な流れは同じですが、UDPではlistenやacceptを使いません。サーバーとクライアントで次のように対になります。

| 順番 | サーバー側                 | クライアント側               |
| -- | --------------------- | --------------------- |
| 1  | socket：端点を作る          | socket：端点を作る          |
| 2  | bind：待ち受けIPとポートを結びつける | （bindは省略。ポートはOSが払い出す） |
| 3  | listen：接続要求の受付を始める    | connect：宛先へ接続する       |
| 4  | accept：接続を1本取り出す      | （ハンドシェイク完了で戻る）        |
| 5  | recv・send：データをやり取りする  | send・recv：データをやり取りする  |
| 6  | close：閉じる             | close：閉じる             |

### PythonのTCP通信：最小サーバーとクライアントの実行例

上の表の順番をそのまま書いたのが次のコードです。Python標準の`socket`モジュールだけで動きます。まずサーバーを起動し、別の端末でクライアントを実行します。

```
# echo_server.py
import socket

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # 1. socket
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", 50050))                            # 2. bind
srv.listen()                                              # 3. listen
print("待ち受け中", srv.getsockname())
conn, addr = srv.accept()                                 # 4. accept
print("接続:", addr, "待受fd", srv.fileno(), "通信fd", conn.fileno())
while True:
    data = conn.recv(1024)                                # 5. recv
    if not data:                                          # 空＝相手がclose
        break
    conn.sendall(data)                                    # 5. send
conn.close()                                              # 6. close
srv.close()
```

```
# echo_client.py
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 1. socket
sock.connect(("127.0.0.1", 50050))                        # 3. connect
print("自分の端点:", sock.getsockname(), "相手:", sock.getpeername())
sock.sendall(b"ping")
reply = bytearray()
while len(reply) < 4:
    chunk = sock.recv(4 - len(reply))
    if not chunk:
        raise ConnectionError("応答の途中で接続が終了しました")
    reply.extend(chunk)
print("応答:", bytes(reply))
sock.close()
```

macOSで実行した出力は次のとおりです。クライアントの送信元ポート（63766）はOSが払い出したもので、bindしていません。

```
待ち受け中 ('127.0.0.1', 50050)
接続: ('127.0.0.1', 63766) 待受fd 3 通信fd 4
自分の端点: ('127.0.0.1', 63766) 相手: ('127.0.0.1', 50050)
応答: b'ping'
```

`recv`が空のバイト列を返したら、相手が接続を閉じた合図です。`send`は渡したデータの一部しか送らないことがあり、戻り値で送れたバイト数を返します。全部を送り切るまで繰り返す`sendall`を使うのが安全です（[Pythonのsocketモジュール公式ドキュメント](https://docs.python.org/3/library/socket.html)）。

### acceptが返すのは待ち受けとは別のソケット

`accept`は、待ち受け用のソケットとは別の、その相手専用のソケットを新しく作って返します。待ち受け用は受付窓口として残り、データのやり取りは接続ごとのソケットで行います。Linuxで`ss -tan`を実行すると、待ち受け中は1行、接続後は3行に増えるのが分かります。

```
$ ss -tan    # 接続前
LISTEN 0 128 127.0.0.1:50007 0.0.0.0:*
$ ss -tan    # 接続後
LISTEN 0 128 127.0.0.1:50007 0.0.0.0:*
ESTAB  0 0   127.0.0.1:50007 127.0.0.1:55508
ESTAB  0 0   127.0.0.1:55508 127.0.0.1:50007
```

ESTABの2行は、サーバー側の通信用ソケットとクライアント側のソケットです。macOSには`ss`が無いので、`netstat -an -p tcp`で同じ状態（LISTEN・ESTABLISHED）を確認できます。

### listenのbacklogの既定値と上限

`listen`の引数backlogは、ハンドシェイクを終えて`accept`を待っている接続を何本まで溜めるかの指定です（[listen(2)](https://man7.org/linux/man-pages/man2/listen.2.html)）。上の`ss`の出力でLISTEN行の3列目にある128がこの値で、Pythonは引数を省くと「SOMAXCONNと128の小さい方」を使います（[CPythonのsocketmodule.c](https://github.com/python/cpython/blob/3.12/Modules/socketmodule.c)の`Py_MIN(SOMAXCONN, 128)`）。

OS側の上限はLinuxが`net.core.somaxconn`で、カーネル5.4から既定が4096（それ以前は128）です。実測でも4096でした。macOSの`kern.ipc.somaxconn`は128です。上限を超える値を渡してもエラーにはならず、黙って上限に切り詰められます。接続が急に集中するサーバーでbacklogを大きくするなら、OS側の上限も合わせて上げないと効きません。

### ブロッキングとノンブロッキングの選び方

既定のソケットはブロッキングで、`accept`や`recv`はデータが来るまでスレッドを止めます。少数の接続では1接続1スレッドが選択肢になります。接続数の増加に伴ってスレッドのメモリ消費や切り替えが負荷になる場合は、ソケットをノンブロッキングにして、準備のできたソケットだけをOSに教えてもらう方式に切り替えます。仕組みはOSごとに違い、Linuxは`epoll`、macOSやFreeBSDは`kqueue`、どこでも使える古い方式が`select`です。Pythonの`selectors`モジュールやNode.js、Goのランタイムはこの違いを吸収しているので、自分で`epoll`を直接呼ぶ場面は多くありません。受信側の処理が追いつかないときの流量制御は[バックプレッシャーの仕組み](/tech/details/16432/)も参考になります。

## UDPソケットでconnect()を呼ぶ意味：宛先の固定とICMPエラーの受け取り

UDPは接続を張らないのに、`connect`を呼べます。Linuxの[connect(2)](https://man7.org/linux/man-pages/man2/connect.2.html)によると、UDPソケットに`connect`すると、指定したアドレスが既定の送信先になり、受信もそのアドレスからのものだけに絞られます。パケットは1つも送られません。送信元ポートはこの時点で決まり、`getsockname()`で確認できます。

実用上の違いは、相手のポートが閉じているときに表れます。閉じたポート（127.0.0.1の50009番）にUDPで1バイト送り、1秒のタイムアウトで応答を待った結果が次の表です。

| 送り方             | Linux（Python 3.12） | macOS（Python 3.9） |
| --------------- | ------------------ | ----------------- |
| connectせずsendto | タイムアウト             | タイムアウト            |
| connectしてsend   | ECONNREFUSED（111）  | ECONNREFUSED（61）  |

相手のホストは「ポート到達不能」のICMPを返しています。Linuxのカーネル（`net/ipv4/udp.c`の`__udp4_lib_err`）は、`IP_RECVERR`を有効にしていない限り、このエラーを接続済みのUDPソケットにだけ渡します。[udp(7)](https://man7.org/linux/man-pages/man7/udp.7.html)には「未接続のソケットでもネットワークからの非同期エラーを返す」という記述がありますが、既定の設定で実測した結果はタイムアウトでした。1つの相手とだけやり取りするUDPクライアント（DNSの問い合わせや社内の計測エージェントなど）では、`connect`しておくと、相手が落ちていることをタイムアウトを待たずに知れます。不特定の相手から受けるUDPサーバーでは`connect`しません。

なお「connect-udp」という語は、HTTPのプロキシ経由でUDPを通すための別の仕組み（RFC 9298「Proxying UDP in HTTP」）で使うアップグレードトークンの名前で、ここで扱うソケットの`connect`とは関係ありません。

## LinuxとmacOSで挙動が違うソケットの落とし穴（実測）

ソケットAPIは共通でも、待ち時間や再利用の条件はOSの実装で違います。開発機がMac、本番がLinuxの場合は、TIME\_WAITやアドレス再利用条件の違いを確認します。

### TIME\_WAITの残り時間：Linuxは約60秒、macOSは約30秒

TCP接続を先に閉じた側のソケットは、TIME\_WAITという状態でしばらく残ります。遅れて届いた古いパケットが、同じアドレスとポートの次の接続に紛れ込まないための待ち時間です。RFC 9293はこの時間を2MSL（MSLは2分とされ、2MSLで4分）としていますが、実装はこれより短くしています。接続を閉じてからTIME\_WAITの行が消えるまでを計ったところ、**Linuxは約61.0秒、macOSは約30.6秒**でした。

| 項目            | Linux（カーネル6.8）             | macOS 26.6.2                 |
| ------------- | -------------------------- | ---------------------------- |
| TIME\_WAITの実測 | 約61.0秒                     | 約30.6秒                       |
| 決めている値        | TCP\_TIMEWAIT\_LEN（60秒・固定） | net.inet.tcp.msl＝15000ms（×2） |
| sysctlの設定項目   | 無い（tcp\_fin\_timeoutは別物）   | net.inet.tcp.msl             |

Linuxの60秒はカーネルのソース（`include/net/tcp.h`の`TCP_TIMEWAIT_LEN (60*HZ)`）に定数で書かれています。よく「`tcp_fin_timeout`を下げればTIME\_WAITが短くなる」と書かれますが、[カーネルのip-sysctl文書](https://docs.kernel.org/networking/ip-sysctl.html)のとおり、この値はFIN\_WAIT\_2状態の保持時間で、TIME\_WAITの長さは変わりません。TIME\_WAITの再利用を許す`tcp_tw_reuse`は、既定値が2（ループバック通信だけで有効）でした。

TIME\_WAITが溜まって困るのは、短い接続を高い頻度で張り直すクライアント側です。対策の第一は接続を使い回すこと（HTTPのキープアライブ、DBのコネクションプール）で、カーネルの設定を変えるのはその後です。

### SO\_REUSEADDRによるアドレス再利用：Linuxの旧・新ソケットへの設定条件

サーバーを止めてすぐ起動し直すと、`bind`が「Address already in use」（EADDRINUSE）で失敗することがあります。原因の一つは、サーバー側から先に接続を閉じ、待ち受けポートを使うTIME\_WAITのソケットが残ることです。別プロセスが同じアドレスで待ち受けていないかも確認します。対策として`SO_REUSEADDR`を付けますが、どのソケットに付ければよいかがOSで違いました。古いサーバーのソケット（旧）と、起動し直したソケット（新）のそれぞれで付ける・付けないを変えて、`bind`の結果を見た表です。

| 旧ソケット | 新ソケット | Linux      | macOS      |
| ----- | ----- | ---------- | ---------- |
| 付けない  | 付けない  | EADDRINUSE | EADDRINUSE |
| 付けない  | 付ける   | EADDRINUSE | 成功         |
| 付ける   | 付けない  | EADDRINUSE | EADDRINUSE |
| 付ける   | 付ける   | 成功         | 成功         |

macOSでは起動し直す側だけに付ければ通りますが、Linuxでは前回のプロセスも付けていないと通りません。この差はLinuxの[socket(7)](https://man7.org/linux/man-pages/man7/socket.7.html)にも、前のプログラムと再利用するプログラムの両方で設定が要り、FreeBSDなどは後のプログラムだけでよい、と書かれています。Macで「新しい側に付けたら直った」と確認しても、本番のLinuxでは最初の再起動で失敗します。待ち受けるソケットには最初から必ず付けておくのが確実です。Pythonの`socket.create_server()`はPOSIX環境で`SO_REUSEADDR`を自動で付けます（Linuxの実測で`getsockopt`が1を返しました）。

### 送信元ポートの払い出し範囲：Linuxは32768〜60999

クライアントの送信元ポートは、OSが決まった範囲から払い出します。[RFC 6335](https://www.rfc-editor.org/rfc/rfc6335.html)は0〜1023をSystem Ports（ウェルノウンポート）、1024〜49151をUser Ports（登録済みポート）、49152〜65535をDynamic Ports（エフェメラルポート）としていて、macOSはこの範囲（`net.inet.ip.portrange`が49152〜65535）でした。一方Linuxの既定は`ip_local_port_range`が32768〜60999で、IANAの区分とは一致しません。

この範囲の広さとTIME\_WAITの長さで、同じ宛先（IPアドレスとポートの組）へ張り直せる接続の頻度に目安が出ます。Linuxは28,232個のポートを60秒間使えないので、TIME\_WAITを再利用しない場合、同じ宛先へ毎秒約470本を超えて新しい接続を張り続けると、送信元ポートが尽きて`connect`が失敗し始めます。負荷試験で急にエラーが出るときは、ここを疑ってください。Linuxで自作サービスの待ち受けポートを32768〜60999の中に置くと、送信元ポートの払い出しとぶつかることがある点にも注意が要ります。

## NAT・ロードバランサーで無通信の接続が切られる問題とキープアライブ

NATやロードバランサーは、通過中の接続を対応表で覚えています。無通信が続くと対応表から消され、両端のプログラムが生きていても次の送受信で失敗します。AWSでは、[NAT Gatewayを通る接続は350秒以上アイドルだとタイムアウト](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-troubleshooting.html)し、[ALBのアイドルタイムアウトは既定60秒](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html)です。

NAT Gatewayについては、AWSの文書が「接続に通信を流すか、350秒未満の間隔でTCPキープアライブを有効にする」ことを対策に挙げており、タイムアウト後に送ろうとするとNAT GatewayがRSTを返します。TCPキープアライブは、OSが無通信の接続に小さな確認パケットを送る仕組みです。ただしLinuxの既定（[tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)）は、最初の確認を送るまで7200秒、間隔75秒、9回応答が無ければ切断で、350秒には間に合いません。長時間つなぎっぱなしにするクライアントでは、ソケットごとに`TCP_KEEPIDLE`を300秒未満などに下げるか、アプリケーション側で定期的にpingを送ります。データベースのドライバーやHTTPクライアントの多くは、この間隔を設定項目として持っています。

ALBはこれと条件が違い、アイドルタイムアウトの前に少なくとも1バイトのデータを送受信する必要があります。HTTP/2のPINGフレームでもALBのアイドルタイムアウトはリセットされないと、[ALBの属性の文書](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html)に書かれています。ALBの先で長く接続を保つなら、アプリケーションのデータとしてpingを流すか、アイドルタイムアウトの設定値を延ばします。AWS上でNAT Gatewayやロードバランサーを含めたネットワーク構成を見直すときは、[AWS・Google Cloud・Azureのインフラ構築支援](/service/system/aws/)でも相談を受けています。

## ソケット通信とHTTP通信・WebSocketの違い

### HTTPとソケットの関係：HTTP/1.1・HTTP/2とHTTP/3の違い

ソケット通信とHTTP通信は、並べて比べる対象ではなく上下の関係です。HTTP/1.1やHTTP/2は、TCPソケットの上に「メソッド・パス・ヘッダー・本文」という書式を決めたプロトコルです（HTTP/3はUDPの上のQUICを使います）。HTTP/1.1では、ブラウザやHTTPクライアントが内部でソケットを開き、必要に応じてTLSを重ねて`GET / HTTP/1.1`のような文字列を書き込んでいます。「ソケット通信」と言うときは、こうした既存の書式に乗らず、自分でデータの区切りや手順を決めて通信する場面を指すことが多く、その分、前の章で見た区切りの設計や再接続を自分で作ります。

### HTTP/1.1でのWebSocket接続：Upgradeによる切り替え

WebSocketは、名前に「ソケット」と入っていますが、生のTCPソケットではありません。最初にHTTPで`Upgrade: websocket`を付けた要求を送り、サーバーが`101 Switching Protocols`を返すと、同じTCP接続の上でWebSocketの形式のフレームをやり取りする方式に切り替わります（[RFC 6455](https://www.rfc-editor.org/rfc/rfc6455.html)）。このHTTP/1.1方式はTCP上で動きます。ただしRFC 9220には、UDP上のQUICを使うHTTP/3でWebSocketを運ぶ方式も定義されています。メッセージの区切りはWebSocketのフレームが受け持つので、TCPのように自分で長さを付ける必要はありません。通常のWebページのJavaScriptには生のTCP・UDPソケットAPIが公開されていないため、サーバーとの双方向通信にはWebSocketが選択肢になります。用途によってはWebRTCのデータチャネルも利用できます。ハンドシェイクと実装は[WebSocketの仕組みとNode.js実装](/tech/details/15815/)、SSEとの使い分けは[SSEとWebSocketの違いと使い分け](/tech/details/4539/)で解説しています。

## 言語ごとのソケットAPIの呼び名

呼び出しの順番はどの言語でも同じで、名前とまとめ方が違うだけです。C言語のAPIがBSD UNIXで生まれた元の形で、ほかの言語はそれを包んでいます。

| 言語              | 待ち受け                    | 接続を受ける          | 接続する                      |
| --------------- | ----------------------- | --------------- | ------------------------- |
| C（sys/socket.h） | socket・bind・listen      | accept          | socket・connect            |
| Python（socket）  | socket.create\_server   | accept          | socket.create\_connection |
| Java（java.net）  | new ServerSocket(port)  | accept          | new Socket(host, port)    |
| Go（net）         | net.Listen              | Listener.Accept | net.Dial                  |
| Node.js（net）    | net.createServer・listen | connectionイベント  | net.createConnection      |

JavaやGo、Node.jsは、bindとlistenを1回の呼び出しにまとめています。C言語で書くときは、`sockaddr_in`構造体にアドレスとポートを詰め、ポート番号を`htons`でネットワークバイトオーダーに変換してから`bind`・`connect`に渡します。Pythonの`socket.socket()`はC言語の関数とほぼ1対1に対応しているので、C言語の解説を読むときの対照にも使えます。

## よくある質問

### ソケット通信とTCP通信は何が違いますか？

TCPは「確実に順番どおり届ける」ための通信規約（プロトコル）で、ソケットはそのTCPやUDPをプログラムから使うための窓口です。TCP通信をするプログラムは、ソケットを通じてTCPを使っています。ソケット通信と言った場合は、TCPだけでなくUDPやUNIXドメインソケットを使う通信も含みます。

### ポート番号はどの範囲を使えばよいですか？

自作サービスの待ち受けには、RFC 6335の登録済みポート（1024〜49151）のうち、既存のサービスと重ならない番号を選びます。0〜1023はウェルノウンポートで、Linuxでは既定で管理者権限が無いとbindできません。Linuxでは送信元ポートの払い出しに32768〜60999が使われるので、この範囲を待ち受けに使うのも避けた方が安全です。

### UNIXドメインソケットとは何ですか？

同じホストの中のプロセス同士で通信するためのソケットで、IPアドレスとポートの代わりにファイルのパス（例：`/var/run/docker.sock`）を住所にします。TCP/IPの処理を通らないので、同じマシン上のWebサーバーとアプリケーションサーバーやデータベースをつなぐときに使われます。作り方と注意点は[ソケットの実装入門](/tech/details/17511/)の章で扱っています。

### ソケット通信は暗号化されていますか？

されていません。TCPソケットで送ったバイト列は、そのまま経路を流れます。インターネットを通すなら、ソケットの上にTLSを重ねます。Pythonなら`ssl`モジュールの`SSLContext.wrap_socket`で既存のソケットを包めます。TLSの仕組みと版の選び方は[TLSとは？SSLとの違い・仕組みとバージョン選定](/tech/details/13318/)を参照してください。

### Windowsでソケット通信を書くときの違いは何ですか？

WindowsのソケットAPIはWinsock（Windows Sockets 2）と呼ばれ、[BSD UNIXのソケットの方式](https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-start-page-2)を元にしているので、socket・bind・listen・accept・connectの流れは同じです。違いは、どのWinsock関数よりも先に[WSAStartup](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsastartup)を呼ぶ必要があることと、閉じるのが`closesocket`であることです。PythonやJavaから使う場合は、ランタイムがこの違いを吸収します。

## 関連記事

- [ソケットとは？Pythonで作って動かすTCP・UDP・UNIXドメインの実装入門](/tech/details/17511/)
- [TCP/IPとは？4階層モデルとOSI参照モデルの違いを実装目線で解説](/tech/details/13083/)
- [WebSocketとは？仕組み・ハンドシェイクからNode.js実装・運用の判断まで実装者向けに解説](/tech/details/15815/)
- [TLSとは？SSLとの違い・仕組みとバージョン選定を実装者向けに解説【2026年最新】](/tech/details/13318/)
- [OSI参照モデルとは？7階層の役割・各層プロトコル・TCP/IPとの違いを解説](/tech/details/8201/)

---

出典: [ソケット通信とは？TCP/UDPソケットの仕組み・API手順とOSで違う落とし穴を実測で解説](<https://www.issoh.co.jp/tech/details/14680/>)（株式会社一創）
