.NET FrameworkのSocketクラスでUDP Multicast Group Socket通信を行う - データ受信編

ずっと前に、Windows Phone 7とWindows PCをUDP Multicast Group Socket通信で連携させる方法をポストしました。このポストでは、Windows PC→Windows Phone 7のデータの流れのみ説明していました。このポストでは、Windows Phone 7がMulticast Groupに送信したデータをWindows PCで受信する方法を解説します。Windows PC側で使うのは.NET FrameworkのSocketクラスです。

Multicast Group通信を、おさらいしておくと、Wi-Fiなどで同じローカルネットに接続されたデバイス間で、アドホックに1 to Manyで通信するためのプロトコルです。グループIPアドレス(224.0.0.1~239.255.255.255が利用可能。一部WS-*で予約されているものもあり)とグループポート番号をあらかじめ決めておいて、そのアドレス、ポートにデータをUDPで送信すると、そのグループにJoinしているデバイスだけに、一括してデータを送信できます。このプロトコルを使えば、Ad-Hocにネットにつながったデバイスを発見して、Peer-To-Peerでつないだりすることが出来ます。例えばどこかのWi-Fiスポットで、そのネットにつながっているデバイス間でチャットしたり、家庭のWi-Fiに家電をつないで、即、家のHEMSやPC,スマホと連携、AV機器連携などにも使えます。1度の送信でグループにJoinしている全てのデバイスにデータが送信できるので、プログラムもシンプルです。このプロトコルは、Wi-Fiルータのローカルネット内で閉じているので、インターネット上にデータは出て行きません。ローカルネット内のデバイスをグループ化してローカルで済む通信に対してはこのプロトコルを使い、必要なデータ送受信・サービスの利用のときのみクラウドと連携といったシナリオも可能です。

能書きはこれぐらいにして、早速Socketを使った受信方法を解説します。通信相手として、Windows Phone 7をご用意ください(シミュレーターでもOKです)。そして、Windows Phone 7側は、https://msdn.microsoft.com/ja-jp/library/ff431744(v=vs.92).aspx から公開されている”マルチキャストソケットのサンプル”の、左側のアイコンの右横にあるリンクからC#/VBのサンプルをダウンロードして動かしておいてください。グループアドレス、グループポートは、このサンプルで使われている、224.0.0.1と52274を使います。

Windows側のコードを解説していきます。

まず、Multicast GroupにJoinするためのコードですが、

    IPAddress groupAddress = IPAddress.Parse("224.0.1.1");
    int groupPort = 52274;

    Socket receiveSocket = new Socket(
                AddressFamily.InterNetwork,
                SocketType.Dgram,
                ProtocolType.Udp);

    receiveSocket.SetSocketOption(
               SocketOptionLevel.Socket,
               SocketOptionName.ReuseAddress,
              1);

    IPEndPoint ipep = new IPEndPoint(
              IPAddress.Any,
              groupPort);

    receiveSocket.Bind(ipep);

    receiveSocket.SetSocketOption(
              SocketOptionLevel.IP,
              SocketOptionName.AddMembership,
              new MulticastOption(groupAddress, IPAddress.Any));

 まず、Multicast Group通信用のSocketインスタンスをひとつ作成します。UDPなので、引数は、上のとおり。次にSetSocketOptionメソッドで、アドレスの再利用を設定します。Multicast Group通信のデータ送信デバイスはネットにAd-Hocにつながった時にIPアドレスが確定し、そのアドレスでデータを送信してくるので、IPアドレスはなんでも”Any”を、ポートは、グループ用ポートで、IPEndPoint変数を作成し、そのEnd Pointで、ソケットをBindします。

そして、SetSocketOptoin()メソッドで、AddMembershipを設定します。この時、SetSocketOption()メソッドの3番目の引数としてMulticastOption変数を作成して使います。Multicast Group通信用のグループアドレスは、MulticastOptionコンストラクタの1番目の変数に指定します。

ここまで実行すると、Multicast GroupへのJoinが完了します。

次にデータの受信方法です。コードは以下の通り。

    IPEndPoint receivePoint = new IPEndPoint(IPAddress.Any, 0);
    EndPoint tempReceivePoint = (EndPoint)receivePoint;

    byte[] packet = new byte[512];
    int length = receiveSocket.ReceiveFrom(packet, 0, packet.Length, SocketFlags.None, ref tempReceivePoint);

    string packetS = System.Text.UTF8Encoding.UTF8.GetString(packet, 0, length);
    System.Diagnostics.Debug.Print(packetS);

受信には、SocketクラスのReceiveFromメソッドを使います。受信用のbyte[]型のバッファを用意して、データは様々なIPアドレスのデバイスから送られてくるので、IPAddress.AnyでEndPointを作っておき、ReceiveFromメソッドをコールします。
ReceiveFromメソッドをコールする行でブレークポイントを張り、このコードを実行して、ブレークしたら、F10でステップ実行します。誰かがデータをMulticast Groupに送信するまで、実行がブロックされます。この状態でWindows Phone 7のMulticast Groupソケットサンプルから、文字列を送信してみてください。

すると、実行ブロックが解けます。UTF8エンコーディングでバイト列のデータをStringに戻してやると、Windows Phone 7側で送信したデータが格納されているのが分かるでしょう。ReceiveFromメソッドの3番目の引数がref指定されています。ReceiveFrom()の実行が完了した時点で、tempReceivePointに、受信したデータを送信したデバイスのIPアドレスが格納されています。この変数を確認すれば、どのデバイスがデータを送ったかも判明します。Multicast GroupにJoinしている間は、自分がMulticast Groupに送信したデータも受信してしまうので、自他の区別に使えます。

最初のコードのBind()実行完了以降でも、Multicast Groupへのデータ送信は可能です。

    receiveSocket.SendTo(
             System.Text.UTF8Encoding.UTF8.GetBytes("WPF App Join"),
             new IPEndPoint(groupAddress, groupPort));

データ送信では、Multicast Group通信をルーターを幾つまで到達させるかを指定することも可能です。

  receiveSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 1);

通常は1です。このコードを実行すると、Multicast Groupからのデータ受信が出来なくなるので、気をつけてください。

Multicast Group通信の使用ケースを考えると、あまり、厳密なハンドシェークはそぐわず、メッセージを受信するループを回して受信データを処理しつつ、必要があればデータ送信、というスタイルが多いと思われるので、送信用、受信用二つのSocketを用意して使うのがよいと思われます。

最後にMulticast Groupから抜ける方法を紹介します。

    receiveSocket.SetSocketOption(
           SocketOptionLevel.IP, SocketOptionName.DropMembership,
           new MulticastOption(groupAddress, IPAddress.Any));

    receiveSocket.Close();

Groupを抜けるには、SetSocketOptionメソッドで、DropMembershipを指定します。このメソッドコール後、Multicast Groupに送信されたデータはとまります。それ以上Socketが必要ないならClose()メソッドをコールして終了です。

この仕組みは実際にDPWS(Device Profile for Web Service)のWindowsの実装である、WSD(Web Service on Device)でも、デバイスのAd-HocなDiscoveryで使われています。

今後、Wi-Fiが利用できる場所は増え、それにつながるスマホやタブレット、PC、デバイスはどんどん増えてきます。ネットワークといえばクラウドと短絡的に考えずに、ローカルネット内だけで十分な通信は、この方法の活用を考えてみてくださいね。