電気ひつじ牧場

技術メモ

Rustにっき/10日目・UDP

10日目です。UDP周りについてやっていきます。

サーバ(UDP

use std::net::UdpSocket;
use std::thread;

fn main() {
    let server_socket = UdpSocket::bind("127.0.0.1:12345")
                           .expect("Could not bind socket");
    loop {
        let mut buf = [0u8; 1024];
        // クロージャの中に移動されるからクローンする。
        let client_socket = server_socket.try_clone().expect("failed to clone socket");

        match server_socket.recv_from(&mut buf) {
            Ok((_, src)) => {
                thread::spawn(move || {
                    println!("handling data from {}", src);
                    client_socket.send_to(&buf, src).expect("failed to send response");
                });
            },
            Err(e) => {
                eprintln!("could not recieve a datagram: {}", e);
            }
        }
    }
}

UDPの場合はTCPとは異なりlistenacceptのようなものはありません。server_socketとしてサーバーソケットで待ち受け、データを受信したら別スレッドでclient_socketを利用し受信元にそのまま送り返すといった処理の流れになります。UDPサーバーのくせにクライアントソケット?と思うかもしれませんが、サーバーソケット、クライアントソケットという用語については

ソケットプログラミング HOWTO — Python 3.7.2 ドキュメントが詳しいです。

loopの中にあるsocket.try_clone()socketと同じアドレス、ポートに対してバインドされたソケットを返却していますが、これはthread::spawn() に渡すクロージャに対してソケットの所有権が移動してしまうからです。

bufのサイズとして1024を指定していますが、この値は想定する受信データによって適当に決定します。今回の場合は1024バイトを超えるデータを受信した場合は1025バイト目以降のデータは破棄されます。

クライアント(UDP

use std::net::UdpSocket;
use std::{str, io};
use std::time::Duration;

fn main() {
    let socket = UdpSocket::bind("127.0.0.1:54321").expect("failed to bind socket");
    socket.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
    socket.set_write_timeout(Some(Duration::from_secs(2))).unwrap();

    loop {
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();

        socket.send_to(input.as_bytes(), "127.0.0.1:12345").expect("failed to send data");

        let mut buffer = [0u8; 1024];
        socket.recv_from(&mut buffer).expect("failed to receive");
        print!("{}", str::from_utf8(&buffer).expect("failed to convert to String"));
    }
}

UDPTCPのようにコネクションを張らないので、送受信用のソケットを作成したら宛先(127.0.0.1:12345) に対してデータを送りつけるだけです。