我目前正在從事一個研究專案,該專案涉及從外部掃描儀接收實時資料(最多 10 個周期/秒)并使用 Unity 在 HoloLens 2 上顯示它。目前,該系統分為兩部分:
- Python 服務器通過 TCP 套接字發送帶有來自外部掃描器的資料的資料包
- HoloLens 客戶端獲取資料包的處理資料并將其顯示給用戶
HoloLens 部分被構建為一個異步套接字客戶端,一旦它接收到資料包,它將相應地更新一些 UI 和 GameObject 組件。我發送的所有資料都是使用我自己的資料包結構的簡單 JSON 字串,并且我已經能夠通過 HoloLens 通過套接字成功接收掃描儀的資料,但似乎一旦掃描儀開始發送太多資料 HoloLens 停止接受新資料包并停止處理任何內容。
這很奇怪,因為在我的計算機上的 Unity 編輯器中運行相同應用程式的測驗沒有顯示這些問題。我知道這是一個競態/無限輪詢條件,但我目前正在使用 C#Socket.BeginReceive() API 來異步等待新資料,根據我對 MSDN 檔案的理解,這應該使用單獨的執行緒處理任何傳入資料。我不明白 HoloLens 上的問題出在哪里,因為除錯日志中沒有拋出例外,并且系統使用較小的資料大小(接近每秒 2 個周期)。另一個想法可能是套接字的緩沖區可以容納 >1 個資料包并且無法正確決議,但這不會產生 Newtonsoft.Json 例外嗎?
這是我的 HoloLens 客戶端的片段:
public class Manager : MonoBehaviour
{
public string serverSocketEndpoint = "localhost";
public string serverCommandEndpoint = "localhost:5000/command";
private Dictionary<string, PacketCategoryData> _tagCounts;
private bool _connected = false;
private static Socket _socket;
private byte[] _socketBuffer = new byte[8192];
private Thread _socketThread;
void Start()
{
StartSocketConnection();
}
public void StartSocketConnection() {
try
{
IPAddress ipAddr = Dns.GetHostAddresses(serverSocketEndpoint)[1];
IPEndPoint writeEndPoint = new IPEndPoint(ipAddr, 12345);
IPEndPoint readEndPoint = new IPEndPoint(ipAddr, 12346);
_socket = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_socket.NoDelay = true;
_socket.ReceiveBufferSize = 8192;
_socket.Connect(writeEndPoint);
Debug.Log(String.Format("Socket connected to {0} ", _socket.RemoteEndPoint.ToString()));
_socket.BeginReceive(_socketBuffer, 0, _socketBuffer.Length, SocketFlags.None, OnDataReceived, _socket);
}
catch (Exception e)
{
Debug.Log(e.ToString());
debugMenu.UpdateStats();
}
}
private void OnDataReceived(IAsyncResult result)
{
Socket connection = result.AsyncState as Socket;
int received = connection.EndReceive(result);
Debug.Log("Got packet data");
// Handle received data in buffer.
string incomingData = Encoding.ASCII.GetString(_socketBuffer, 0, received);
DataPacket packet = JsonConvert.DeserializeObject<DataPacket>(incomingData);
// Start a new async receive on the client to receive more data.
_socket.BeginReceive(
_socketBuffer,
0,
_socketBuffer.Length,
SocketFlags.None,
OnDataReceived,
_socket);
// Parse the previous packet that we picked up
ParseIncomingPacket(packet);
}
public void ParseIncomingPacket(DataPacket packet)
{
try
{
if (packet != null)
{
if (packet.type.Equals("count_update"))
{
// Do some data processing and UI updating on the main thread...
// This particular place is where things go wrong, or atleast this general area
// Until I surpass the threshold of about 2 or 3 cycles a second, everything is
// being processed correctly, but as soon as I surpass that limit everything just stops
// with no errors
UnityMainThreadDispatcher.Instance().Enqueue(() =>
{
Logger.Log("[Info] Got count_update response");
});
_connected = true;
var json = JsonConvert.SerializeObject(packet.data);
Dictionary<string, PacketCategoryData> categoryCounts =
JsonConvert.DeserializeObject<Dictionary<string, PacketCategoryData>>(json);
// Since this is Unity game object specific code, it must be ran on the main thread
UnityMainThreadDispatcher.Instance().Enqueue(CountDataCallback());
}
}
else
{
//
// Exception conditions...
//
}
}
catch (Exception e)
{
UnityMainThreadDispatcher.Instance().Enqueue(() =>
{
Debug.LogError(String.Format("Unexpected Exception : {0}", e.ToString()));
_connected = false;
debugMenu.UpdateStats();
});
}
}
private IEnumerator CountDataCallback() {
UpdateTagCountUI();
debugMenu.UpdateStats();
yield return null;
}
}
任何幫助將不勝感激。
編輯:我正在使用一個外部庫,UnityMainThreadDispatcher因為 BeginRecieve 在不同的子執行緒上運行,Unity 只接受在主執行緒上進行的游戲物件/Unity 特定的方法呼叫。這是為了規避這個限制,并允許在我們收到資料包后更新我的場景物件/UI
uj5u.com熱心網友回復:
正如評論中提到的,我可以想象問題是您的外部庫以非常高的幀速率發送了大量的更新
=> 您的 Unity 應用程式將所有這些更新收集到Queuewhih 中,然后必須在其中UnityMainThreadDispatcher迭代所有這些更新
private void Update()
{
while (_executionQueue.Count > 0)
{
_executionQueue.Dequeue().Invoke();
}
}
所以想象一下在一幀內從你的外部庫接收 10 個更新
=> 你的 Unity 應用程式必須使用 10 次冗余迭代來更新 UI,因為無論如何只有最后一個對用戶可見!
同時它會延遲這一幀 => 在下一幀中,您將更多的專案堆積到Queue
=> 外部庫向其發送更新資料包的時間越長,您的整個應用程式就越陳舊。
在您的用例中,您似乎并不真正關心資料的確保順序和連續接收和處理。
您更想要的是您的 UI 僅處理和顯示最新接收到的資料狀態。
因此,我不會使用 aQueue而是使用 aConcurrentStack并且只處理最近收到DataPacket的為此幀,否則基本上復制調度程式的結構,例如
// A thread-safe Stack
private readonly ConcurrentStack<DataPacket> _lastReceivedDataPackets = new ();
private void Update()
{
if(_lastReceivedDataPackets.TryPop(out var dataPacket))
{
// TODO: Put your UI update here
UpdateUIWithReceivedData(dataPacket);
// We don't care about any previous received data -> simply erase them
_lastReceivedDataPackets.Clear();
}
}
private void ParseIncomingPacket(DataPacket packet)
{
...
// whatever is the last call to this before the next frame will determine what is displayed in the next frame
_lastReceivedDataPackets.Push(packet);
...
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/520782.html
上一篇:套接字僅接收46位元組的資料
