• Hololens开发笔记:UDP接收数据


    Hololens的应用需要与其他设备通信的时候,UDP是比较方便的一种方式,Unity3d 2017.3 C#开发的时候可以用Windows.Networking.Sockets.DatagramSocket 类来接收、发送UDP消息,简单快捷。

    .net 的System.net.Socket对象应该也可以,但是只支持部分方法,一开始没走通所以我没继续测试下去。

    开发的时候注意两个地方:

    1、DatagramSocket.MessageReceived 事件不在主线程运行,所以在这个事件中不能操作UI的东西,比如给文本框赋值,否则报错。

    2、Unity Build的时候Player Settings要勾选 PrivateNetworkClientServer,注意跟InternetClientServer不一样。因为我是在局域网测试,一开始只是勾选了InternetClientServer,结果无法接收消息。

    ---------------------------------------

    下面是网上找的一个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截图:

  • 相关阅读:
    Android中查找一个Layout中指定的子控件
    常用代码
    数据库连接池配置
    分享一个电子书网站
    怎么快速入手一个项目在没有人指导的情况下
    压测如何观测jvm,就是使用jmx来实现jvm监控
    工具类
    APP开发和web开发的区别
    网站切流量
    互联网主题分析
  • 原文地址:https://www.cnblogs.com/shawy/p/8760387.html
Copyright © 2020-2023  润新知