1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

G23【外伝】TCP/UDPの違いがわかる:Slack・IPMsg・オンラインゲームを例に、接続とポートを整理

Last updated at Posted at 2026-01-21

連載Index(読む順・公開済リンクはここが最新): S00: 門前の誓い(総合Index)

TCPとUDPの違いが、ふわっとしか説明できない。
HTTPはTCP?オンラインゲームはUDP?じゃあSlackやTeamsは何が動いている?

ポート番号の話になると、急に置いていかれる。
NATとかファイアウォールとか、聞いたことはあるのに言葉が繋がらない。

このページでは「TCP/UDP=何が違うのか」「どのアプリでどう使われるのか」を、
用語→例→手を動かす順で整理する。


このページで手に入るもの

  • TCP/UDPの違いを「届き方」と「遅れ方」で説明できるようになる
  • ソケット(IPアドレス+ポート番号+TCP/UDP)の見方が分かる(例つき)
  • Slack / Teams / IPMsg / オンラインゲーム / Windows Update で「どう使われるか」を整理できる
  • 先に逆引き(やりたいこと→TCP/UDP→注意点)が1枚で見える
  • C#で最小の通信を試せる:TCPはローカルで接続→送受信、UDPは送受信(コメント多め)
  • 有名どころポート一覧(TCP/UDP込み)を用途で引ける+外部一覧リンクも置く

先に逆引き(やりたいこと→TCP/UDP→注意点)

ここは「どっちを選ぶか」を用途から決められる形にする。

やりたいこと まず選ぶ 理由(1行) 注意点
Webを見る(HTTP/HTTPS) TCP 欠けると困るので再送が効く方が自然 企業ネットワークではプロキシが間に入ることがある
チャット/業務ツール(Slack/Teams) TCPが主 欠けると困るメッセージを確実に運ぶ 443/TCPへ集約されやすい。宛先制限やSSL検査で止まることがある
LAN内で相手を探す(IPMsgなど) UDPが出やすい 一斉通知(ブロードキャスト)と相性が良い セグメントや設定で届かない。応答が無い構成もある
リアルタイム(オンラインゲーム/通話) UDPが出やすい 遅延より「今」を優先しやすい 到達の確定が難しい。必要ならアプリ側で補う
ファイル転送/更新配布(Windows Updateなど) TCP 欠けると困るデータを運ぶ 途中断は再開や検証が必要。回線が混むと遅くなる
時刻同期(NTP) UDPが多い 小さなパケットを軽くやり取り 取りこぼし前提で繰り返す設計が多い

まず揃える用語(ここで止まらないための最小セット)

ここから先で用語が引っかかりやすいので、先に押さえる言葉をまとめる。
「単語が分からない」だけで読み進めづらくなる箇所を先に潰しておく。

ソケット(IPアドレス+ポート番号+TCP/UDP)

通信の窓口。実務では「IPアドレス」「ポート番号」「TCPかUDPか」の組で考える。
同じIP・同じポート番号でも、TCPとUDPは別物になる。

  • 例(IPアドレス+ポート番号)
    • 172.16.1.1:8080(宛先が 172.16.1.1、ポートが 8080)
    • 127.0.0.1:18080(ローカルPCの 18080。サンプルの接続先に使う)
  • 例(プロトコルまで含めて確定する)
    • TCP 172.16.1.1:8080
    • UDP 172.16.1.1:8080(数字が同じでも別物)

.NETでの表現は次のようになる(例)。

using System.Net;

// 例: 172.16.1.1:8080 をエンドポイントとして持つ
var ep = new IPEndPoint(IPAddress.Parse("172.16.1.1"), 8080);

ポート番号

同じPC(同じIP)で複数アプリが通信できるように分ける番号。
“443” のような数字は、TCP/UDPまで含めて初めて意味が確定する。

ファイアウォール(FW)

通信を通す/落とすルール。宛先IP・ポート番号・TCP/UDPの別などで判定する。
番号だけの会話だとズレやすい。

NAT(家庭用ルータでよく出る)

家庭用ルータなどが、内部アドレスを外へ出す時に変換する仕組み。
変換の状態は永続ではなく、時間経過で消えることがある(放置後に途切れる原因になりやすい)。

NATタイプ(ゲーム機や一部サービスで出てくる表示)

ゲーム機や一部サービスでは、接続の“通りやすさ”をタイプで表示することがある。
Sony系の表示(Type 1/2/3)と、Open/Moderate/Strict の表示は意味が近いものとして扱われることが多い。

  • Open / Type 1:制限が少ない(NAT無し相当の構成を含む)
  • Moderate / Type 2:一般的な家庭用ルータ構成で通る範囲
  • Strict / Type 3:制限が強く、相手との組み合わせで繋がりにくいことがある

プロキシ

HTTP/HTTPSの通信を中継する仕組み。Webは通るが、別経路の通信だけ止まる、が起きやすい。

ネットワークの混雑(回線や経路が混み合う状態)

回線や経路が混み合い、遅延が増えたり再送が増えたりする状態。
「夕方だけ遅い」などの形で見えやすい。


TCPとUDPの違い(届き方と遅れ方で整理する)

ここは、TCP/UDPを「運び方の性格」として整理する。

TCP(欠けたら埋める)

TCPは、順序通りに届け、欠けた分は再送で埋める前提がある。
その分、混雑時は待ちが積み上がって遅くなることもあるが、「欠けない」方が大事な通信に合う。

  • 接続を張ってから送る(相手の存在を確かめた上で進む)
  • 取りこぼしがあれば再送される(届くまで粘る動きになる)
  • 混雑が増えると、再送や待ちが増えて遅くなりやすい

UDP(投げっぱなし)

UDPは、投げて終わり。届いたかどうかは、基本的に確定できない。
その代わり軽いので、遅延を小さくしやすい。リアルタイム系や探索で出やすい。

  • 接続を張らずに送れる(状態が薄い)
  • 応答がないことも普通にある(確実に到達を確認できない)
  • NATやWi-Fi品質の影響が、時間帯や距離で「遅い/欠ける/途切れる」に出やすい

どうしてUDPでも正常に動くのか(理屈を整理する)

ここは「届いたか確定できないのに動く」理由を整理する。

1) “最新だけ分かれば良い”通信がある

オンラインゲームの位置・視点・状態などは、次の更新がすぐ来る。
1つ欠けても、次の更新で上書きできる。
この場合、再送して遅れるより「今の情報が早く来る」方が価値が高い。

  • 欠けても致命傷になりにくいデータをUDPへ乗せる
  • 欠けた分は次の更新で回復する設計にする

2) 重要なものだけ“別扱い”にする

ゲームでも、ログインや購入、重要な操作などは欠けると困る。
こういう部分はTCPに乗せるか、UDPでもアプリ側で確認応答・再送を用意する。

  • 重要操作:確実性が必要 → TCP、またはアプリ側で確認応答
  • 状態更新:遅延が困る → UDP(欠けても次で上書き)

3) “探索”は一斉通知(ブロードキャスト)で回数を稼げる

LAN内探索は「届く相手がいれば返してくれれば十分」。
一斉通知(ブロードキャスト)で投げ、返ってきたものだけ拾う動きが多い。
全員に確実に届ける必要が無いので、UDPが噛み合う。


OSI参照モデル(通信を整理する概念図)

OSI参照モデルは、通信を層に分けて考える“概念上の整理枠”。
ネットワーク機器の中に、そのまま実体として存在するものではない。
ただ、用語や切り分けの会話を整理する助けになる。

  • TCP/UDPは トランスポート層(IPの上)
  • ポート番号はトランスポート層の概念

アプリで見るTCP/UDP(どう使われるかに着目)

ここは「名前を聞いたことがあるアプリ」で、使われ方のイメージを作る。
個別アプリの設定手順や攻略には踏み込まず、TCP/UDPの使われ方に焦点を当てる。

Slack / Teams(企業ネットワークで通る経路に集まりやすい)

業務ツールは「欠けると困る」通信が多く、TCPが主になりやすい。
加えて、企業ネットワークで通りやすい経路(HTTPS 443/TCP)へ集約されやすい。

  • 使われ方
    • メッセージや通知を確実に運ぶ(欠けると困る)
    • 長時間の接続を維持し、通知を受け取りやすくする
  • 見え方
    • ブラウザは開けるのにアプリが落ちる、などはプロキシ/宛先制限/SSL検査の影響が混ざりやすい

IP Messenger(LAN内で相手を見つける・呼びかける)

LAN内で相手を探す用途は、一斉通知(ブロードキャスト)と相性が良い。
そのためUDPが絡む構成になりやすい。

  • 使われ方
    • 一斉通知で呼びかけ、返ってきた相手だけとやり取りする
  • 見え方
    • 同一セグメントだと見えるが、VLANや設定で見えなくなる

オンラインゲーム(動きの情報は早さが大事で、ログインや購入みたいな重要操作は確実さが大事)

リアルタイム性が強い領域は、UDPが出やすい。
一方で、重要操作はTCPや確認応答が必要になりやすい。

  • 使われ方
    • 状態更新はUDPで高速に回す(欠けても次で上書きできるデータが中心)
    • 重要操作は確実性を確保する(TCPまたは確認応答)
  • 見え方
    • Wi-Fiやルータの状態で、時間帯や距離で不安定さが出やすい

Windows Update(欠けると困るデータを確実に運ぶ)

更新配布は、欠けたら破綻するデータを扱う。
そのためTCPで確実に運ぶ構成が自然になる。

  • 使われ方
    • 大きなデータを分割して確実に取得し、検証して適用する
  • 見え方
    • 回線が混むと遅くなる。途中断でも再開の仕組みが必要になる

有名どころポート一覧(用途で覚える)

ここは「用途→番号→TCP/UDP」をまとめる。番号だけ覚える運用はズレやすい。

用途 ポート TCP/UDP ひとこと
HTTP 80 TCP Web(平文)
HTTPS 443 TCP Web(暗号)/企業ネットワークで通りやすい
DNS 53 UDP/TCP 通常はUDP、応答が大きいとTCPも出る
SSH 22 TCP リモートログイン
SMTP 25 TCP メール送信(サーバ間)
Submission 587 TCP メール送信(クライアント→サーバ)
IMAP 143 / 993 TCP メール受信(993は暗号)
POP3 110 / 995 TCP メール受信(995は暗号)
FTP(制御) 21 TCP 制御用。データ側で別ポートが増える
SFTP 22 TCP SSH上でファイル転送(FTPとは別物)
NTP 123 UDP 時刻同期
DHCP 67/68 UDP IP配布(サーバ/クライアント)
RDP 3389 TCP リモートデスクトップ
SMB 445 TCP ファイル共有(Windows)

ここで詰まりやすいポイントは2つ。

  • 同じ番号でもTCPとUDPは別物(ルールも別になる)
  • FTPは制御とデータが分かれ、データ側で別ポートが増える

外部一覧(網羅で当たりたい時)も置いておく。


C#でTCPとUDP通信を試す(ローカルで完結)

ここは、TCPとUDPの違いを“手を動かして”確認するための最小サンプルを置く。
ネットワークの学習が主目的なので、C#側は入門的な書き方をそっと置く程度に留める。
接続先が無いと試せない問題を避けるため、TCPはローカル(127.0.0.1:18080)でサーバとクライアントを用意し、同じPC内だけで往復できる形にする。
UDPも同様に、ローカルで往復確認できるようにする。


最短サンプル:TCP(ローカルEchoで接続→送受信)

ここは「TCPは接続を張ってから送る」を確認できる最小構成。
ローカルPC内だけで完結するので、社内ネットワークや外部環境に左右されにくい。

  • サーバ:127.0.0.1:18080 で待ち受け、受け取った文字列をそのまま返す
  • クライアント:127.0.0.1:18080 へ接続し、文字列を送って返りを表示する

TCPサーバ(Echo)

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

internal static class TcpEchoServer
{
    /// <summary>
    /// TCPで待ち受けし、受け取った文字列をそのまま返す簡易Echoサーバ。
    /// </summary>
    /// <remarks>
    /// 試す目的:TCPは「接続を張る→送る→返る」という流れになる点を確認する。
    /// 接続先:127.0.0.1:18080(ローカルPC内で完結)
    /// </remarks>
    public static async Task RunAsync(IPAddress listenAddress, int listenPort, CancellationToken ct)
    {
        // TcpListenerは「TCPで待ち受けする」ためのクラス
        var listener = new TcpListener(listenAddress, listenPort);

        // Startで待ち受け開始(ここでポートを掴む)
        listener.Start();

        try
        {
            while (!ct.IsCancellationRequested)
            {
                // AcceptTcpClientAsyncで「接続してきたクライアント」を受け取る(TCPは接続が前提)
                var client = await listener.AcceptTcpClientAsync(ct).ConfigureAwait(false);

                // 接続は複数来るので、1接続=1処理として別タスクで処理する
                _ = Task.Run(() => HandleClientAsync(client, ct), ct);
            }
        }
        catch (OperationCanceledException)
        {
            // 終了要求で抜ける
        }
        finally
        {
            // Stopで待ち受け終了(ポート開放)
            listener.Stop();
        }
    }

    /// <summary>
    /// 1クライアント分の送受信を処理する。
    /// </summary>
    private static async Task HandleClientAsync(TcpClient client, CancellationToken ct)
    {
        // usingで接続を確実に閉じる(TCPは接続の後始末が重要)
        using (client)
        {
            // NetworkStreamはTCP接続上のバイト列の読み書き
            using var stream = client.GetStream();

            // Reader/Writerで文字列として扱う(UTF-8)
            using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true);
            using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 1024, leaveOpen: true) { AutoFlush = true };

            // 1行受け取る(クライアントが改行で区切って送る想定)
            var line = await reader.ReadLineAsync(ct).ConfigureAwait(false);

            // 受け取った内容をそのまま返す(Echo)
            if (line is not null)
            {
                await writer.WriteLineAsync(line).ConfigureAwait(false);
            }
        }
    }
}

TCPクライアント(接続→送る→返りを読む)

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

internal static class TcpEchoClient
{
    /// <summary>
    /// TCPで接続し、1行送って、返ってきた1行を受け取る。
    /// </summary>
    /// <remarks>
    /// 試す目的:TCPは接続を張ってから通信する点、返りが「同じ接続」上で戻る点を確認する。
    /// 接続先:127.0.0.1:18080(TcpEchoServerと揃える)
    /// </remarks>
    public static async Task<string?> RoundTripAsync(string host, int port, string message, int timeoutMs = 3000)
    {
        using var cts = new CancellationTokenSource(timeoutMs);

        // TcpClientは「TCPで接続する」ためのクラス
        using var client = new TcpClient();

        // ConnectAsyncで接続を張る(ここが成立しないと送れない)
        await client.ConnectAsync(host, port, cts.Token).ConfigureAwait(false);

        using var stream = client.GetStream();
        using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true);
        using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 1024, leaveOpen: true) { AutoFlush = true };

        // 1行送る(サーバ側はReadLineで受ける)
        await writer.WriteLineAsync(message).ConfigureAwait(false);

        // 返りを1行読む(サーバ側は同じ内容をWriteLineで返す)
        return await reader.ReadLineAsync(cts.Token).ConfigureAwait(false);
    }
}

最短サンプル:UDP(ローカルで送る→返す→一致確認)

ここは「UDPは投げっぱなしで、到達の確定が難しい」を前提に、往復で確かめる最小構成。
UDPは“応答が無い構成”も多いので、疎通確認は返す側を用意して往復で確定する。

  • サーバ:127.0.0.1:18081 で待ち受け、受け取ったバイト列をそのまま返す
  • クライアント:127.0.0.1:18081 へ送って、同一内容が返るかを見る

UDPサーバ(Echo)

using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

internal static class UdpEchoServer
{
    /// <summary>
    /// UDPで受けたデータを、そのまま送り返す簡易Echoサーバ。
    /// </summary>
    /// <remarks>
    /// 試す目的:UDPは接続を張らずに送れる一方、到達の確定が難しい点を「往復」で確認する。
    /// 待受:127.0.0.1:18081(ローカルPC内で完結)
    /// </remarks>
    public static async Task RunAsync(int listenPort, CancellationToken ct)
    {
        if (listenPort < 1 || listenPort > 65535) throw new ArgumentOutOfRangeException(nameof(listenPort));

        // UdpClientはUDP送受信のためのクラス
        using var udp = new UdpClient(listenPort);

        while (!ct.IsCancellationRequested)
        {
            UdpReceiveResult recv;

            try
            {
                // ReceiveAsyncで「1つのデータグラム」を受け取る(UDPはメッセージ単位)
                recv = await udp.ReceiveAsync(ct).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                break;
            }

            // 受け取った内容を、そのまま送り返す(Echo)
            await udp.SendAsync(recv.Buffer, recv.Buffer.Length, recv.RemoteEndPoint).ConfigureAwait(false);
        }
    }
}

UDPクライアント(送る→返りを待つ→一致確認)

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

internal static class UdpEchoClient
{
    /// <summary>
    /// UDPで送信し、指定時間内に同一内容の応答が返るかを確認する。
    /// </summary>
    /// <remarks>
    /// 試す目的:UDPは送った側で到達を確定できない点を、Echoの往復で確認する。
    /// 接続先:127.0.0.1:18081(UdpEchoServerと揃える)
    /// </remarks>
    public static async Task<bool> RoundTripAsync(string host, int port, string message, int timeoutMs = 2000)
    {
        if (string.IsNullOrWhiteSpace(host)) throw new ArgumentException("host is empty.", nameof(host));
        if (port < 1 || port > 65535) throw new ArgumentOutOfRangeException(nameof(port));
        if (timeoutMs < 1) throw new ArgumentOutOfRangeException(nameof(timeoutMs));

        var payload = Encoding.UTF8.GetBytes(message ?? string.Empty);

        using var udp = new UdpClient();

        // Connectは「宛先を既定にする」だけで、TCPの接続確立とは別物
        udp.Connect(host, port);

        // 1つのデータグラムとして送る
        await udp.SendAsync(payload, payload.Length).ConfigureAwait(false);

        using var cts = new CancellationTokenSource(timeoutMs);

        try
        {
            // 返りを待つ(返らない場合も普通にあり得るのでタイムアウトを持つ)
            var recv = await udp.ReceiveAsync(cts.Token).ConfigureAwait(false);

            // 返ってきた内容が一致するかを確認
            var echoed = Encoding.UTF8.GetString(recv.Buffer);
            return string.Equals(echoed, message ?? string.Empty, StringComparison.Ordinal);
        }
        catch (OperationCanceledException)
        {
            return false;
        }
        catch (SocketException)
        {
            return false;
        }
    }
}

TCP/UDPで起きやすい困りごと(起き方→対策)

ここは、TCP/UDPを選んだ後に長引きやすいポイントを短くまとめる。

1) ポート番号は合っているのに、TCP/UDPの違いで通らない

  • 起き方
    「443を開けた」で話が進み、実際は TCP 443 だけ通って UDP 443 が落ちている(または逆)
  • 対策
    ポート番号に加えて TCP か UDP かまで含めて確認する(設定とログも同じ観点で見る)
  • 目印
    同じポート番号なのに、片方だけ通る/片方だけ落ちる

2) UDPの無応答を、そのまま不通と決める

  • 起き方
    送ったが返らない → すぐ不通と判断して切り分けが止まる(応答が無い設計もある)
  • 対策
    返す側を用意して往復で確かめる(Echoのような最小構成で確認する)
  • 目印
    仕様として「返さない」構成が含まれている

3) NATの状態が消えて、しばらくすると途切れる

  • 起き方
    最初は動くが、放置後に途切れる/再接続が必要になる
  • 対策
    時間経過を含めて観測する。必要なら KeepAlive や再送の設計を検討する
  • 目印
    起動直後は正常でも、時間が経つと不調になることがある

4) 回線や経路が混み合い、遅くなる

  • 起き方
    夕方などで遅くなる。再送が増え、待ちが積み上がる
  • 対策
    タイムアウト、再試行、サイズ、同時接続数を見直す。まず状況をログで残す
  • 目印
    時間帯で遅延が変わる/大きいデータで顕著になる

チェックリスト(レビューで見る所)

ここは、設計と実装の確認観点をまとめる。

観点 見るポイント よくあるズレ
選択理由 欠けると困るか、遅延が困るかが言語化されているか “なんとなくUDP” “なんとなくTCP”
ルール ポート番号にTCP/UDPの別が付いているか 番号だけで会話してズレる
タイムアウト TCP接続/読み書き、UDP受信待ちが明示されているか 待ち続けて固まる
再試行 再送が必要な所に方針があるか(冪等も含む) 二重送信で整合が崩れる
ログ どこで止まったか(名前解決/接続/受信待ち)が残るか “繋がらない” だけが残る
NAT 放置後の挙動が観測できるか “最初だけOK” を見逃す

セルフチェック(5問)

  1. TCPとUDPの違いを「欠けたら埋める」「投げっぱなし」で説明できるか
  2. ソケットを「IPアドレス+ポート番号+TCP/UDPの別」で説明できるか(例つきで言えるか)
  3. 同じポート番号でもTCPとUDPが別物だと説明できるか
  4. UDPが届いたか確定できないのに動く理由を、更新の上書き・重要操作の別扱いで説明できるか
  5. NATが時間経過で効いてくる理由を説明できるか
回答の目安
  • 1: TCPは欠けた分を再送で埋める。UDPは投げっぱなしで到達の確定が難しい
  • 2: 例は TCP 172.16.1.1:8080 / UDP 172.16.1.1:8080。数字が同じでも別物になる
  • 3: ルールや設定は「番号+TCP/UDP」で別。番号だけだとズレる
  • 4: 状態更新は次の更新で上書きできる。重要操作はTCPか確認応答で確実性を確保する
  • 5: NATは変換の状態が永続ではない。放置後に途切れる問題が出ることがある

関連トピック


連載Index(読む順・公開済リンクはここが最新): S00: 門前の誓い(総合Index)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?