如何在 UWP 应用程序中读取来自服务器的字节数组?

How can I read byte array coming from server in UWP app?

我有一个客户端-服务器应用程序。客户端是 HoloLens 2,服务器是 unity 运行 在 PC 上。我使用 HoloLens 创建了一个套接字,但无法读取 HoloLens 上的字节数组。在connect方法中_inputStream和_outputStream是System.IO.Streams.

PC 客户端使用 System.Net.Sockets.TcpClientSystem.Net.Sockets.NetworkStream 但 UWP (HoloLens) 使用 Windows.Networking.Sockets.StreamSocket.

在 ReceiveCallback 中,我有一个问题 int byteLenght = _inputStream.EndRead(result); 当我在 HoloLens 上尝试这段代码时,我得到了异常:“System.ArgumentException:指定的 AsyncResult 不对应于任何未完成的 IO 操作”。但我用 PC 客户端 (System.Net.Socket) 测试了它,一切正常。

顺便说一句,我给了所有相关权限。

如何在 UWP 应用程序中读取字节数组?

更新:

#if UNITY_EDITOR
        private void ConnectUWP(string host, string port)
#else
        private async void ConnectUWP(string host, string port)
#endif
        {
#if UNITY_EDITOR
            errorStatus = "UWP TCP client used in Unity!";
#else
        try
        {
            socket = new Windows.Networking.Sockets.StreamSocket();
            Windows.Networking.HostName serverHost = new Windows.Networking.HostName(host);
            _receivedData = new Packet();
            _receiveBuffer = new byte[DataBufferSize];
            await socket.ConnectAsync(serverHost, port);
            successStatus = "Connected!";

             _outputStream = socket.OutputStream.AsStreamForWrite();
             //_streamWriter = new StreamWriter(_outputStream) { AutoFlush = true };
             
             _inputStream = socket.InputStream.AsStreamForRead();
             Task.Run(() => StreamToBytes(socket.InputStream));

        }
        catch (Exception e)
        {
            errorStatus = e.ToString();
            Debugger.GetComponent<TextMeshPro>().text += "\n Exception: " + errorStatus;
        }
#endif

#if !UNITY_EDITOR
        public async Task StreamToBytes(Windows.Storage.Streams.IInputStream stream)
        {
             using (var reader1 = new Windows.Storage.Streams.DataReader(stream))
             {
                reader1.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.ReadAhead;
                reader1.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                reader1.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;

                //InBuffer is always 256, 
                //even if there is more data waiting. If I put a task.delay in it will always return 25
                try
                {
                    await reader1.LoadAsync(256);
                }
                catch (Exception ex)
                {
                    //..
                }

                while (reader1.UnconsumedBufferLength > 0)
                {
                    var bytes1 = new byte[reader1.UnconsumedBufferLength];
                    reader1.ReadBytes(bytes1);
                    _receivedData.Reset(await HandleData(bytes1));
                    await reader1.LoadAsync(256); 
                }
                reader1.DetachStream();
              }
        }

            private async Task<bool> HandleData(byte[] data)
            {
                int packetLength = 0;

                _receivedData.SetBytes(data);

                if (_receivedData.UnreadLength() >= 4)
                {
                    // If client's received data contains a packet
                    packetLength = _receivedData.ReadInt();

                    if (packetLength <= 0)
                    {
                        // If packet contains no data
                        return true; // Reset receivedData instance to allow it to be reused
                    }
                }

                while (packetLength > 0 && packetLength <= _receivedData.UnreadLength())
                {
                    // While packet contains data AND packet data length doesn't exceed the length of the packet we're reading
                    byte[] packetBytes = _receivedData.ReadBytes(packetLength);
                    
                    ThreadManager.ExecuteOnMainThread(() =>
                    {
                        using (Packet packet = new Packet(packetBytes))
                        {
                            int packetId = packet.ReadInt();
                            _packetHandlers[packetId](packet); // Call appropriate method to handle the packet
                        }
                    });

                    packetLength = 0; // Reset packet length
                    if (_receivedData.UnreadLength() >= 4)
                    {
                        // If client's received data contains another packet
                        packetLength = _receivedData.ReadInt();
                        if (packetLength <= 0)
                        {
                            // If packet contains no data
                            return true; // Reset receivedData instance to allow it to be reused
                        }
                    }
                }

                if (packetLength <= 1)
                {
                    return true; // Reset receivedData instance to allow it to be reused
                }

                return false;
            }
#endif

When I tried this code on HoloLens I got the exception: "System.ArgumentException: The specified AsyncResult does not correspond to any outstanding IO operation".

请参考BeginRead方法文档,在.NET Framework 4及更早版本中,必须使用BeginRead、EndRead等方法来实现异步I/O操作.这些方法在 .NET Framework 4.5 中仍然可用,以支持遗留代码;但是,新的异步方法,例如 ReadAsync、WriteAsync、CopyToAsync 和 FlushAsync,可以帮助您更轻松地实现异步 I/O 操作。 我想这个旧的 api 无法与 HoloLens 兼容设备,所以请尝试使用 ReadAsync 替换如下

 byte[] bytes = await StreamToBytes(_inputStream);
 public async Task<byte[]> StreamToBytes(Stream stream)
 {
     byte[] bytes = new byte[stream.Length];
     await stream.ReadAsync(bytes, 0, bytes.Length);
     stream.Seek(0, SeekOrigin.Begin);
     return bytes;
 }

或者按照官方的处理使用DataReader读取ReadBytes,更多代码请参考streamsocket.

更新

using (var reader1 = new DataReader(_inputStream)
{
    reader1.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.Partial;
    reader1.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
    reader1.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;

    await reader1.LoadAsync(256); //InBuffer is always 256, 
                                  //even if there is more data waiting. If I put a task.delay in it will always return 25
    while (reader1.UnconsumedBufferLength > 0)
    {
        var bytes1 = new byte[reader1.UnconsumedBufferLength];
        reader1.ReadBytes(bytes1);
        await reader1.LoadAsync(256); 
    }
    reader1.DetachStream();
}

我为 TCP 尝试了一切,但 TCP 对于带有 UWP 应用程序的 HoloLens 几乎是不可能的。所以我尝试了 UDP,它运行良好 (https://github.com/mbaytas/HoloLensUDP)。我希望微软在不久的将来为 HoloLens 1 和 2 提供一个 TCP 示例。感谢 Nico 的帮助。