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

Nov 23, 2017 - 3 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送受信を行っています.

  • 送信側
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System.Net;
using System.Threading;
using System.Net.Sockets;
using System.Text;

public class UdpNetworkClientManager
{
    private UdpClient udpclient;

    public UdpNetworkClientManager(int port)
    {
        udpclient = new UdpClient();
        udpclient.EnableBroadcast = true;
        udpclient.Connect(new IPEndPoint(IPAddress.Broadcast, port));
    }

    public void SendMessage(string data)
    {
        Thread thread = new Thread(() =>
        {
            byte[] bytes = Encoding.UTF8.GetBytes(data);
            udpclient.Send(bytes, bytes.Length);
        });
        thread.Start();
    }
}
  1. UdpClientを作成しブロードキャスト通信の許可とブロードキャストアドレス,ポート番号を指定します.
  2. データ送信時にはSendMessage(string data)から非同期処理によって送信を行います.
  • 受信側
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using System.Text;

public class UdpNetworkListenManager
{
    private Thread thread = null;

    public UdpNetworkListenManager(int port)
    {
        thread = new Thread(()=> {
            UdpClient udpclient = new UdpClient(port);
            IPEndPoint remote = new IPEndPoint(IPAddress.Any, port);
            try
            {
                byte[] bytes = udpclient.Receive(ref remote);
                string ms = Encoding.UTF8.GetString(bytes);
            }
            catch (Exception) { }
            udpclient.Close();
        });
        thread.Start();
    }
}
  1. 送信時と同様に非同期処理中にUdpClientを作成しポートを指定します.
  2. Receiveによってデータを受信しstring msにテキストデータとして受け取ります.

HoloLens編

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

  • 送信側
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Net;
#if UNITY_UWP
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
#endif

public class UdpNetworkClientManager
{
#if UNITY_UWP
    private StreamWriter writer=null;
#endif

    public UdpNetworkClientManager(int port)
    {
#if UNITY_UWP
        Task.Run(async () =>
        {
            DatagramSocket socket = new DatagramSocket();
            string Address = IPAddress.Broadcast.ToString();
            var datagram = await socket.GetOutputStreamAsync(new HostName(Address), port.ToString());
            writer = new StreamWriter(datagram.AsStreamForWrite());
        });
#endif
    }

    public void SendMessage(string data)
    {
#if UNITY_UWP
        if (writer != null) Task.Run(async () =>
        {
            await writer.WriteAsync(data);
            await writer.FlushAsync();
        });
#endif
    }
}
  1. 非同期処理中にDatagramSocketを作成しブロードキャストアドレスとポート番号を指定します.
  2. SendMessage(string data)よりデータを送信します.
  • 受信側

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
#if UNITY_UWP
using System.IO;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
#endif

public class UdpNetworkListenManager
{
#if UNITY_UWP
    private DatagramSocket socket = null;
#endif

    public UdpNetworkListenManager(int port)
    {
#if UNITY_UWP
        Task.Run(async () =>
        {
            socket = new DatagramSocket();
            socket.MessageReceived += MessageReceived;
            await socket.BindServiceNameAsync(port.ToString());
        });
#endif
    }

#if UNITY_UWP
    async void MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
    {
        StreamReader reader = new StreamReader(args.GetDataStream().AsStreamForRead());
        string ms = await reader.ReadLineAsync();
    }
#endif
}

  1. 送信時と同様にDatagramSocketを作成しポートを指定します.
  2. 作成時にデータ受信時に呼ばれるイベントを登録します.
  3. データ受信時にMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)が呼ばれstring msにデータを受け取ります.

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

まとめ

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