---
title: "DartでUDP通信の結果を同期的に待つ方法"
description: "DartでUDP通信の結果を同期的に待つために、FutureとCompleterを組み合わせる方法を紹介します。"
category: "Web"
tags: ["Dart","UDP","Completer","Future","非同期処理"]
publishedAt: "2024-05-02"
lastmod: "2026-04-14"
---

`Future` と `Completer` を組み合わせることで、DartでUDP通信の受信結果を同期的に待機し、後続処理に値を返せます。UDP通信を同期的に扱うこと自体は推奨しませんが、ユースケースによっては必要になる場面があります。

## サンプルサーバーを用意する

今回使う通信先として、以下のRubyコードを実行しておきます。

```ruby
require 'socket'

socket = UDPSocket.new
socket.bind('localhost', 5001)

loop do
  data, addr = socket.recvfrom(1024)
  puts "Received: #{data} from #{addr[2]}:#{addr[1]}"

  sleep 3 # 待機

  response = "Hello, #{data}!"
  socket.send(response, 0, addr[2], addr[1])
  puts "Sent: #{response} to #{addr[2]}:#{addr[1]}"
end
```

リクエストデータを元に `Hello, #{data}!` の形式で返す簡単なサーバーです。受信から送信まで **3秒間** の待機（`sleep 3`）を入れています。このサーバーと通信するDartコードを実装していきます。

## CompleterでUDP受信を待機する

UDP通信には `RawDatagramSocket` を使用します。`RawDatagramSocket` は送受信を非同期で扱えますが、今回は同期的に結果を待ち、値を呼び出し元に返せるように `request` 関数と `main` 関数を実装します。

```dart
import 'dart:convert';
import 'dart:async';
import 'dart:io';

Future<String> request(InternetAddress address, int port, String name) async {
  final socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
  final completer = Completer<String>();

  try {
    final data = utf8.encode(name);
    socket.send(data, address, port);

    socket.listen((RawSocketEvent event) {
      if (event == RawSocketEvent.read) {
        final datagram = socket.receive();
        if (datagram != null) {
          final message = utf8.decode(datagram.data);
          socket.close();
          completer.complete(message);
        }
      }
    });
  } catch (e) {
    print('Error: $e');
    socket.close();
    completer.completeError(e);
  }

  return completer.future;
}

Future<void> main() async {
  try {
    final result = await request(InternetAddress.loopbackIPv4, 5001, 'Bob');
    print('Received: $result');
  } catch (e) {
    print('Error: $e');
  }
}
```

`main` 関数から `request` 関数を呼び出し、通信先からのレスポンスを標準出力しています。

## Completerの動作を読む

`request` 関数で同期的に待機するための処理は以下の流れです。

1. `Completer<String>` を作成する。非同期処理の完了時に文字列を返すために使用する、今回の実装の要です
2. `socket.listen` で受信イベントをリッスンする
3. 受信データをデコードし、`completer.complete` に渡す。これでデータが `Future` の結果として設定される
4. `completer.future` で `Future<String>` を返す

`main` 関数側では返ってきた `Future` を `await` するだけです。`completer.complete` で設定した文字列データが取得でき、実質的にUDP通信の受信を同期的に扱える状態になります。

## Completerパターンの応用

`Completer` を使ったパターンはUDP通信に限った話ではありません。コールバックベースのAPIを `Future` に変換したい場面で広く使えます。