なんかいろいろしてみます

Nov 23, 2017 - 2 minute read - HoloLens

HoloLensでSharingする方法(UDP編)

前回のTCP編の続きとしてUDP通信によるShring通信に関しても書いておきます.

UDPでの利点,欠点

TCPを使ったネットワーク通信ではサーバー側のIPアドレスをクライアント側が知っておく必要がありました. そのためローカルホスト内のネットワーク通信でもネットワーク環境が変わるとサーバーアドレスを入力する必要が出てきます. またTCPは通信方式の関係で通信上の遅延が発生する場合があります.

UDP通信ではデータ受信の確認を行わないためTCPより遅延が少ない可能性があります. また通信相手を指定せずにローカルネットワークすべてにデータを送信するブロードキャスト送信が行えるため,送信先アドレスを知らなくてもネットワーク通信が行えます.

しかし,UDP通信ではローカルネットワークのセキュリティ上通信を許可していない場合があります. UDPによるブロードキャスト通信には

  • 送信側デバイスがUDPブロードキャスト送信を許可している
  • 通信を中継するルーターがUDP通信とブロードキャスト送受信を許可している
  • 受信側デバイスがUDPブロードキャスト受信を許可している

以上の条件を確認する必要があります. Windowsではさらに

  • ファイアーウォールが通信用ソフトウェアのUDP送受信を許可している

を確認する必要があります. またポート番号だけはあらかじめ指定しておく必要があります.

自前で実装

UDP通信でもTCPと同様にUnityで利用できるC#関数とHoloLensなどUWPで利用できるC#関数が異なります.

Unity編

Unity側では前回同様Threadによる非同期処理を行い,UdpClientによるUDP送受信を行っています.

  • 送信側
 1using System.Net;
 2using System.Threading;
 3using System.Net.Sockets;
 4using System.Text;
 5
 6public class UdpNetworkClientManager
 7{
 8    private UdpClient udpclient;
 9
10    public UdpNetworkClientManager(int port)
11    {
12        udpclient = new UdpClient();
13        udpclient.EnableBroadcast = true;
14        udpclient.Connect(new IPEndPoint(IPAddress.Broadcast, port));
15    }
16
17    public void SendMessage(string data)
18    {
19        Thread thread = new Thread(() =>
20        {
21            byte[] bytes = Encoding.UTF8.GetBytes(data);
22            udpclient.Send(bytes, bytes.Length);
23        });
24        thread.Start();
25    }
26}
  1. UdpClientを作成しブロードキャスト通信の許可とブロードキャストアドレス,ポート番号を指定します.
  2. データ送信時にはSendMessage(string data)から非同期処理によって送信を行います.
  • 受信側
 1using System;
 2using System.Net;
 3using System.Threading;
 4using System.Net.Sockets;
 5using System.Text;
 6
 7public class UdpNetworkListenManager
 8{
 9    private Thread thread = null;
10
11    public UdpNetworkListenManager(int port)
12    {
13        thread = new Thread(()=> {
14            UdpClient udpclient = new UdpClient(port);
15            IPEndPoint remote = new IPEndPoint(IPAddress.Any, port);
16            try
17            {
18                byte[] bytes = udpclient.Receive(ref remote);
19                string ms = Encoding.UTF8.GetString(bytes);
20            }
21            catch (Exception) { }
22            udpclient.Close();
23        });
24        thread.Start();
25    }
26}
  1. 送信時と同様に非同期処理中にUdpClientを作成しポートを指定します.
  2. Receiveによってデータを受信しstring msにテキストデータとして受け取ります.

HoloLens編

HoloLensなどUWP環境では非同期処理にTask,UDPデータ送受信にDatagramSocketを利用します.

  • 送信側
 1using System.Net;
 2#if UNITY_UWP
 3using System;
 4using System.IO;
 5using System.Threading.Tasks;
 6using Windows.Networking;
 7using Windows.Networking.Sockets;
 8#endif
 9
10public class UdpNetworkClientManager
11{
12#if UNITY_UWP
13    private StreamWriter writer=null;
14#endif
15
16    public UdpNetworkClientManager(int port)
17    {
18#if UNITY_UWP
19        Task.Run(async () =>
20        {
21            DatagramSocket socket = new DatagramSocket();
22            string Address = IPAddress.Broadcast.ToString();
23            var datagram = await socket.GetOutputStreamAsync(new HostName(Address), port.ToString());
24            writer = new StreamWriter(datagram.AsStreamForWrite());
25        });
26#endif
27    }
28
29    public void SendMessage(string data)
30    {
31#if UNITY_UWP
32        if (writer != null) Task.Run(async () =>
33        {
34            await writer.WriteAsync(data);
35            await writer.FlushAsync();
36        });
37#endif
38    }
39}
  1. 非同期処理中にDatagramSocketを作成しブロードキャストアドレスとポート番号を指定します.
  2. SendMessage(string data)よりデータを送信します.
  • 受信側
 1using System;
 2#if UNITY_UWP
 3using System.IO;
 4using System.Threading.Tasks;
 5using Windows.Networking.Sockets;
 6#endif
 7
 8public class UdpNetworkListenManager
 9{
10#if UNITY_UWP
11    private DatagramSocket socket = null;
12#endif
13
14    public UdpNetworkListenManager(int port)
15    {
16#if UNITY_UWP
17        Task.Run(async () =>
18        {
19            socket = new DatagramSocket();
20            socket.MessageReceived += MessageReceived;
21            await socket.BindServiceNameAsync(port.ToString());
22        });
23#endif
24    }
25
26#if UNITY_UWP
27    async void MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
28    {
29        StreamReader reader = new StreamReader(args.GetDataStream().AsStreamForRead());
30        string ms = await reader.ReadLineAsync();
31    }
32#endif
33}
  1. 送信時と同様にDatagramSocketを作成しポートを指定します.
  2. 作成時にデータ受信時に呼ばれるイベントを登録します.
  3. データ受信時にMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)が呼ばれstring msにデータを受け取ります.

HoloLensなどUWP環境ではTCP編同様に通信機能の許可が必要になるため,UnityEditorまたはVisualStudio上で機能の許可を設定してください.

まとめ

  • 実行環境ごとの切り分けが大変
  • UDPブロードキャスト通信ができる環境なら比較的簡単に通信ができる
  • 今回の実装に関しては自前の HoloLensModuleにて複数台接続に対応したものを入れています.