Hololens的應用須要與其餘設備通訊的時候,UDP是比較方便的一種方式,Unity3d 2017.3 C#開發的時候能夠用Windows.Networking.Sockets.DatagramSocket 類來接收、發送UDP消息,簡單快捷。網絡
.net 的System.net.Socket對象應該也能夠,可是隻支持部分方法,一開始沒走通因此我沒繼續測試下去。多線程
開發的時候注意兩個地方:app
一、DatagramSocket.MessageReceived 事件不在主線程運行,因此在這個事件中不能操做UI的東西,好比給文本框賦值,不然報錯。socket
二、Unity Build的時候Player Settings要勾選 PrivateNetworkClientServer,注意跟InternetClientServer不同。由於我是在局域網測試,一開始只是勾選了InternetClientServer,結果沒法接收消息。async
---------------------------------------函數
下面是網上找的一個UDP收發消息的類:測試
using UnityEngine; using System; using System.IO; using System.Text; using System.Linq; using HoloToolkit.Unity; using System.Collections.Generic; using UnityEngine.Events; #if !UNITY_EDITOR using Windows.Networking.Sockets; using Windows.Networking.Connectivity; using Windows.Networking; #endif [System.Serializable] public class UDPMessageEvent : UnityEvent<string, string, byte[]> { } public class UDPCommunication : Singleton<UDPCommunication> { [Tooltip("port to listen for incoming data")] public string internalPort = "12345"; [Tooltip("IP-Address for sending")] public string externalIP = "192.168.17.110"; [Tooltip("Port for sending")] public string externalPort = "12346"; [Tooltip("Send a message at Startup")] public bool sendPingAtStart = true; [Tooltip("Conten of Ping")] public string PingMessage = "hello"; [Tooltip("Function to invoke at incoming packet")] public UDPMessageEvent udpEvent = null; private readonly Queue<Action> ExecuteOnMainThread = new Queue<Action>(); #if !UNITY_EDITOR //we've got a message (data[]) from (host) in case of not assigned an event void UDPMessageReceived(string host, string port, byte[] data) { Debug.Log("GOT MESSAGE FROM: " + host + " on port " + port + " " + data.Length.ToString() + " bytes "); } //Send an UDP-Packet public async void SendUDPMessage(string HostIP, string HostPort, byte[] data) { await _SendUDPMessage(HostIP, HostPort, data); } DatagramSocket socket; async void Start() { if (udpEvent == null) { udpEvent = new UDPMessageEvent(); udpEvent.AddListener(UDPMessageReceived); } Debug.Log("Waiting for a connection..."); /** *關鍵點一:建立使用UDP的網絡通訊對象:DatagramSocket,而後添加接收消息的事件MessageReceived */ socket = new DatagramSocket(); socket.MessageReceived += Socket_MessageReceived; HostName IP = null; try { var icp = NetworkInformation.GetInternetConnectionProfile(); IP = Windows.Networking.Connectivity.NetworkInformation.GetHostNames() .SingleOrDefault( hn => hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId == icp.NetworkAdapter.NetworkAdapterId); /** * 關鍵點二:綁定IP和監聽端口,IP若是傳null好像不行。 */ await socket.BindEndpointAsync(IP, internalPort); } catch (Exception e) { Debug.Log(e.ToString()); Debug.Log(SocketError.GetStatus(e.HResult).ToString()); return; } if (sendPingAtStart) SendUDPMessage(externalIP, externalPort, Encoding.UTF8.GetBytes(PingMessage)); } private async System.Threading.Tasks.Task _SendUDPMessage(string externalIP, string externalPort, byte[] data) { using (var stream = await socket.GetOutputStreamAsync(new Windows.Networking.HostName(externalIP), externalPort)) { using (var writer = new Windows.Storage.Streams.DataWriter(stream)) { writer.WriteBytes(data); await writer.StoreAsync(); } } } #else // to make Unity-Editor happy :-) void Start() { } public void SendUDPMessage(string HostIP, string HostPort, byte[] data) { } #endif static MemoryStream ToMemoryStream(Stream input) { try { // Read and write in byte[] block = new byte[0x1000]; // blocks of 4K. MemoryStream ms = new MemoryStream(); while (true) { int bytesRead = input.Read(block, 0, block.Length); if (bytesRead == 0) return ms; ms.Write(block, 0, bytesRead); } } finally { } } // Update is called once per frame void Update() { /** * 關鍵點四:由主線程觸發事件,避免外面調用的類可能出錯。 */ while (ExecuteOnMainThread.Count > 0) { ExecuteOnMainThread.Dequeue().Invoke(); } } #if !UNITY_EDITOR private void Socket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args) { Debug.Log("GOT MESSAGE FROM: " + args.RemoteAddress.DisplayName); //Read the message that was received from the UDP client. Stream streamIn = args.GetDataStream().AsStreamForRead(); MemoryStream ms = ToMemoryStream(streamIn); byte[] msgData = ms.ToArray(); /** * 關鍵點三:接收到數據用事件發送出去,這裏只是把觸發事件的代碼放到隊列ExecuteOnMainThread中。 * 是由於接收函數是多線程的,跟Unity主線程不同,本身開發的時候若是在這裏給文本框賦值會報錯! */ if (ExecuteOnMainThread.Count == 0) { ExecuteOnMainThread.Enqueue(() => { Debug.Log("ENQEUED "); if (udpEvent != null) udpEvent.Invoke(args.RemoteAddress.DisplayName, internalPort, msgData); }); } } #endif }
而後是Player Settings截圖:ui