🚠

RustでUDPを簡単に実装する

に公開

はじめに

rustでUDPを実装してみます。

マシンスペック

MacBook Air M2 arm64

事前知識

UDPとは

UDPとはOSI参照モデルのトランスポート層のプロトコルです。

トランスポート層では、TCPとUDPの代表的なプロトコルがあります。
TCPは信頼性のある通信、UDPは同報通信すなわちブローだキャストなど、一方的に相手に送りつける通信と捉えていただけると良いと思います。
TCPがコネクション型の通信、UDPはコネクションレス型の通信になります。TCPとUDPに甲乙はつけ難いのですが、用途によって使い分けます。
TCPでは、オンデマンドの動画配信などに使用され、UDPはIP電話やテレビの配信などがあります。リアルタイム性を要求するか否かの認識として問題ないでしょう。

UDPのパケットの構成

UDPは下記の様なパケットの構成をとります。

準備

Dockerの起動

docker run --rm -it --name rust-udp -v "$PWD":/app rust:1.78 bash
# 以降はコンテナ内で作業
apt-get update
apt-get install -y vim

cd /app
cargo new --bin udp_demo
cd udp_demo

ファイルの準備

サーバ側

mkdir -p src/bin

cat <<'EOF' > src/bin/server.rs
use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    // ① 5555/UDP
    let sock = UdpSocket::bind("127.0.0.1:5555")?;
    println!("listening on udp/5555 …");

    let mut buf = [0u8; 1500];  // 1500 = Ethernet MTU
    loop {
        // ② ブロッキング受信(戻り値=(受信バイト数, 送信元アドレス))
        let (n, addr) = sock.recv_from(&mut buf)?;
        println!(
            "main from {} : {}",
            addr,
            String::from_utf8_lossy(&buf[..n])
        );
    }
}
EOF

クライアント側

cat <<'EOF' > src/bin/client.rs
use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    // ①送信専用ソケットを取得
    let sock = UdpSocket::bind("0.0.0.0:0")?;

    // ② 宛先 (127.0.0.1:5555)を固定してconnect
    sock.connect("127.0.0.1:5555")?;

    // ③ 送信
    let msg = b"Hello, UDP!";
    sock.send(msg)?;
    println!("sent '{}' ({} bytes)", String::from_utf8_lossy(msg), msg.len());

    Ok(())
}
EOF

Cargo.tomlの編集

vim Cargo.toml
# 下記を追加する。
[[bin]]
name = "server"
path = "src/bin/server.rs"

[[bin]]
name = "client"
path = "src/bin/client.rs"

実験

2つのターミナルを準備しますので、別のタブで下記のコマンドを打ってDockerに新たに接続してください。以降、サーバ側、クライアント側と呼びます。

docker ps # ここでコンテナIDが出てくるので控えておく。
docker exec -it {CONTAINER_ID} bash

サーバ側

cargo run --release --bin server
   Compiling udp_demo v0.1.0 (/app/udp_demo)
    Finished `release` profile [optimized] target(s) in 0.18s
     Running `target/release/server`
listening on udp/5555

クライアント側

cargo run --release --bin client
    Finished `release` profile [optimized] target(s) in 0.01s
     Running `target/release/client`
sent 'Hello, UDP!' (11 bytes)

まとめ

今回はrustでUDPを実装してみました。学習の補助になれば幸いです。

Discussion