HOLOLENS的SOCKET網絡通訊1
2019/5/23      點擊:
多數開發者開(kāi)發Hololens的通信功能是先想到的是(shì)system.net.socket庫裏的socket,發布UWP的時候就可能出問題,因為UWP對system庫不是完全的支持,很多方法或者類是(shì)沒有(yǒu)定義的(這是一個很(hěn)常見的發布(bù)UWP的報錯)。本(běn)文用的system.net.socket裏(lǐ)的SAEA係列,全稱:SocketAsyncEvnetArgs,這是微軟針對高並(bìng)發而設計的一套API, SAEA是異步的socket參數,使用SAEA時需要注意三點:1.緩衝區  2.IP  3.完成(chéng)後的回調,這三點是必(bì)要(yào)的,其次還有其他的SAEA參數,不是必要的,例如(rú)UserToken等,詳細(xì)可查API。
一般的socket需求用上麵的代碼足夠用的,由於上文(wén)中隻(zhī)有一個接收SAEA和一個發送SAEA,所以當一個SAEA在(zài)工作時,不要再讓這個SAEA工作。
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System;
using System.Text;
//這個腳本是hololens端的(de)SocketUDP腳本,提供發送方法,初始化並開(kāi)啟接收方(fāng)法
public class MyUdpClient : MonoBehaviour
{
    Socket socket; //目標socket
    //發送端口
    EndPoint serverEnd; 
    IPEndPoint ipEnd; 
    //接收端口
    IPEndPoint IPLocalPoint;
    //發(fā)送用的socket異步參(cān)數
    SocketAsyncEventArgs socketAsyceArgs;
    //接收用的socket異步參數(shù)
    SocketAsyncEventArgs reciveArgs;
    //接收SAEA用來接收的緩衝區
    byte[] reciveArgsBuffer;        
    //初始化
    void InitSocket()
    {
        //定義連接的服務器ip和端口(kǒu),可以是本機ip,局域網,互聯網
        ipEnd = new IPEndPoint(ipadsdress.Parse("10.100.172.226"), 8001);
        //初始(shǐ)化要接收的IP,ipadsdress.Any表示接收所(suǒ)有IP地址發來的(de)字(zì)節流
        IPLocalPoint = new IPEndPoint(ipadsdress.Any, 8002);
        //初始化socket
        socket = new Socket(IPLocalPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);  
        //定義服務端
        IPEndPoint sender = new IPEndPoint(ipadsdress.Any, 0);
        serverEnd = (EndPoint)sender;
        //初始化(huà)發送用的SAEA
        socketAsyceArgs = new SocketAsyncEventArgs();
        //設置(zhì)發送用的SAEA的IP
        socketAsyceArgs.RemoteEndPoint = ipEnd;
        //初始化接收用的(de)SAEA的緩衝區,此處我設為10K
        reciveArgsBuffer = new byte[1024 * 10];
        //初始化接收SAEA
        reciveArgs = new SocketAsyncEventArgs();
        //設置接收SAEA的接收IP地址
        reciveArgs.RemoteEndPoint = IPLocalPoint;
        //因為SAEA係列API 是(shì)異步方法,所以設置好完成方法後的回調
        reciveArgs.Completed += new EventHandler(CompletedRecive);
        //設置接收緩衝區
        reciveArgs.SetBuffer(reciveArgsBuffer, 0, reciveArgsBuffer.Length);
    }
    //異步方(fāng)法完成(chéng)後的complete時間
    private void CompletedRecive(object sender, SocketAsyncEventArgs e)
    {
        //通過SAEA.LastOperation這個枚舉來判斷完成的是什(shí)麽方法,對應不同(tóng)的操作
        switch (reciveArgs.LastOperation)
        {
            //因為reciveArgs是我專門用來接(jiē)收的SAEA,所以這裏隻(zhī)設置一個完成接收後用的方法
            case SocketAsyncOperation.ReceiveFrom:
                PocessReceiveFrom(e);
                break;       
        }
    }
    //中轉緩衝(chōng)區,將(jiāng)數據拷貝出來給(gěi)主線程(chéng)用
    byte[] tempBytes;
    //用來通知主線(xiàn)程的參數
    bool isOk=false;
    //注意:處理這個方法是輔線程,不要用Unity的類,否則報錯,將收到的字節流拷貝出來,通知主線程來處理
    //接收(shōu)完成(chéng)後對應(yīng)的處理方法
    public void PocessReceiveFrom(SocketAsyncEventArgs e)
    {
        if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
        {
            //這裏會造成內存垃圾(jī)以及內存碎片化,如果頻(pín)繁的長時間的接收,建議做一個Byte池。
            tempBytes = new byte[e.BytesTransferred];     //將數據(jù)拷(kǎo)貝出來保證可以複用
            Array.Copy(e.Buffer, e.Offset, tempBytes, 0, tempBytes.Length);
            //通知主線程
            isOk = true;
        }
    }
    ////// 異步發送消息方法
    //////public void AsyncSend(byte[] bytes)
    {
        //設(shè)置緩衝區,緩衝區裏是發送的字(zì)節流(liú)
        socketAsyceArgs.SetBuffer(bytes, 0, bytes.Length);
        //Debug.Log("socket異步參數字節流長度 " + socketAsyceArgs.Buffer.Length);
        bool bo = socket.SendToAsync(socketAsyceArgs);
        if (!bo)
        {
            //在hololens上發現過一段時間scoket就(jiù)不會發送(sòng)數據,*後(hòu)這(zhè)樣處理:判(pàn)斷SentToAsync方法失敗後,就重新new一(yī)個SAEA,解決socket發送失敗(bài)的問題(tí)
            //注意初始化一個SAEA時(shí),1.IP    2.緩(huǎn)衝區,3.完成後的回調事件  這三個都是必要的,
            socketAsyceArgs = new SocketAsyncEventArgs();
            socketAsyceArgs.RemoteEndPoint = ipEnd;
        }
    }
    //初始化socket並測試一下
    private void Start()
    {
        InitSocket();
        TestSocekt();
    }
    //用來測試socket的方法,發送一個信息(xī)
    void TestSocekt() {
        int tempInt = 9999;
        byte[] tempBytes;
 
        tempBytes=BitConverter.GetBytes(tempInt);
        AsyncSend(tempBytes);
    }
    private void Update()
    {
        if (isOk)
        {
            //對(duì)tempBytes進行處理
            int temp= BitConverter.ToInt32(tempBytes, 0);
            Debug.Log("接收socket,接(jiē)收到了字節流,接(jiē)收(shōu)到(dào)的數字為(wéi) " + temp);
            isOk = false;
        }
    }
    //每(měi)隔一段時間就接受一(yī)下
    private void FixedUpdate()
    {
        socket.ReceiveFromAsync(reciveArgs);
    }
}
上麵的代碼把接收(shōu)模塊和(hé)發送模塊寫在一(yī)起,SAEA係列是異步(bù)的,所以使用起來對於多(duō)線(xiàn)程(chéng)需要一些了解。一般的socket需求用上麵的代碼足夠用的,由於上文(wén)中隻(zhī)有一個接收SAEA和一個發送SAEA,所以當一個SAEA在(zài)工作時,不要再讓這個SAEA工作。
捷徑:後來發現在MixedRealTooklit裏麵有(yǒu)scoket組件,可以直接使用MRTK中Sharing文件夾中的組件,或者查(chá)看MRTK的源碼,裏麵是(shì)用Windows.Networking和Task寫(xiě)的Socket,找了很長時間的SocketAPI,原來遠在天邊近在眼前,感歎當時怎麽(me)不好好看看MRTK!!
- 上一篇:通過UE4 的 INTEL REALSENSE 插件以新的方 2019/5/28
- 下一篇:UNITY3D設置VS2015調試的方(fāng)法 2019/5/11



