Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么我的套接字输入流会丢失字节?_C#_Sockets_Stream_Uwp_Bytestream - Fatal编程技术网

C# 为什么我的套接字输入流会丢失字节?

C# 为什么我的套接字输入流会丢失字节?,c#,sockets,stream,uwp,bytestream,C#,Sockets,Stream,Uwp,Bytestream,我正在创建一个基于套接字的系统,用于桌面和移动设备之间的通信。我使用一个简单的协议来读取和写入设备之间的数据流,这些数据最终以字节的形式结束: 前4个字节表示一个整数,它定义了要执行的命令类型 接下来的4个字节表示一个整数,它是流中其余字节的长度 剩余字节表示有效负载数据,其总数对应于(2)的结果 我能够成功剥离前4个字节并解析命令ok,然后剥离下4个字节并正确解析长度。 当我去掉剩余的字节时,问题就出现了,其中一些字节丢失了,并且它们从剩余数据的前面丢失了 比如,;如果命令是1,长度是50,那

我正在创建一个基于套接字的系统,用于桌面和移动设备之间的通信。我使用一个简单的协议来读取和写入设备之间的数据流,这些数据最终以字节的形式结束:

  • 前4个字节表示一个整数,它定义了要执行的命令类型
  • 接下来的4个字节表示一个整数,它是流中其余字节的长度
  • 剩余字节表示有效负载数据,其总数对应于(2)的结果
  • 我能够成功剥离前4个字节并解析命令ok,然后剥离下4个字节并正确解析长度。 当我去掉剩余的字节时,问题就出现了,其中一些字节丢失了,并且它们从剩余数据的前面丢失了

    比如,;如果命令是1,长度是50,那么流中应该还剩下50个字节,但只有46个,缺少的是0-3个字节

    起始数据如下所示:

    • 命令:1
    • 长度:50
    • 有效载荷:C:\Users\dave\Music\Offaiah-troub\uu(Club\umix).mp3
    将其转换为字节数组后,我得到:

    “\u0001\0\0\02\0\0\0C:\Users\dave\Music\Offaiah-troub\uu(Club\u Mix).mp3”

    (问题-为什么第一个整数前面有\u000,而第二个没有?)

    下面是我用来解析此代码的一些代码片段:

    IBuffer inbuffer = new Windows.Storage.Streams.Buffer(4);
    await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
    int command = BitConverter.ToInt32(inbuffer.ToArray(), 0);
    
    此时的inbuffer包含:“\u0001\0\0\0”,位转换器将其解析为1

    inbuffer = new Windows.Storage.Streams.Buffer(4);
    await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
    int length = BitConverter.ToInt32(inbuffer.ToArray(), 0);
    
    inbuffer现在包含:“2\0\0\0”,位转换器将其解析为“50”

    inbuffer现在包含:“sers\dave\Music\Offaiah-troub\u(Club\u Mix).mp3”


    前面缺少的“C:\U”是从哪里来的?

    因此,我意识到我一直在投票,因为这个问题不简洁,不可重复。所以我创建了一个小项目来演示问题的这一部分,讽刺的是,问题消失了

    代码如下:

    public sealed partial class MainPage : Page
    {
        private StreamSocketListener _listener;
        private StreamSocket _client;
        private StreamSocket _socket;
        private HostName _host;
        private int _port = 54321;
    
        public MainPage()
        {
            InitializeComponent();
            _listener = new StreamSocketListener();
            _listener.ConnectionReceived += async (sender, args) =>
            {
                _socket = args.Socket;
                await Receive();
            };
            _host = NetworkInformation.GetHostNames().FirstOrDefault(x => x.IPInformation != null && x.Type == HostNameType.Ipv4);
        }
    
        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            await _listener.BindEndpointAsync(_host, $"{_port}");
            await Task.Run(async () =>
            {
                _client = new StreamSocket();
                await _client.ConnectAsync(_host, $"{_port}");
    
                int command = 1;
                byte[] cmd = BitConverter.GetBytes(command);
                byte[] path = Encoding.UTF8.GetBytes(@"C:\Users\Dave\Music\Offaiah-Trouble_(Club_Mix).mp3");
                byte[] length = BitConverter.GetBytes(path.Length);
                byte[] result = cmd.Concat(length.Concat(path)).ToArray();
                await _client.OutputStream.WriteAsync(result.AsBuffer());
            });
        }
    
        private async Task Receive()
        {
            while (true)
            {
                IBuffer inbuffer = new Windows.Storage.Streams.Buffer(4);
                await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
                int command = BitConverter.ToInt32(inbuffer.ToArray(), 0);
                //The inbuffer at this point contains: "\u0001\0\0\0", and the BitConverter resolves this to 1
    
                inbuffer = new Windows.Storage.Streams.Buffer(4);
                await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
                int length = BitConverter.ToInt32(inbuffer.ToArray(), 0);
                //The inbuffer now contains: "2\0\0\0", and the BitConverter resolves this to "50"
    
                inbuffer = new Windows.Storage.Streams.Buffer((uint)length);
                await _socket.InputStream.ReadAsync(inbuffer, (uint)length, InputStreamOptions.Partial);
                string path = Encoding.UTF8.GetString(inbuffer.ToArray());
            }
        }
    }
    
    如果您创建一个新的空白通用项目并运行它,您将看到它生成了正确的输出

    最终我发现我在这个流阅读器和另一个流阅读器之间有竞争条件。在主项目中,我不会一次性读取所有字节。在将控制权传递给另一个方法之前,我有一个单独的方法来读取和解析“命令”,将控制权重定向到几个辅助方法中的一个,以服务于特定的命令-剥离长度,然后剥离剩余的有效负载

    问题是,“命令读取器”随后从流中读取了另外4个字节,然后工作程序才能读取其有效负载

    很明显,答案是它应该在这里暂停并等待worker完成,但我使用的是async并在整个过程中等待,所以我很惊讶我遇到了这个问题。原因是缺少等待和可怕的异步无效,如下所示

    违规代码:

    private async Task Listen()
    {
        while (true)
        {
            //expects a 4 byte packet representing a command
            Debug.WriteLine($"Listening for socket command...");
            IBuffer inbuffer = new Windows.Storage.Streams.Buffer(4);
            await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
            int command = BitConverter.ToInt32(inbuffer.ToArray(), 0);
    
            Debug.WriteLine($"Command received: {command}");
            ParseCommand(command);
        }
    }
    
    private async void ParseCommand(int command)
    {
        //...
    }
    
    …以及最新版本:

    private async Task Listen()
    {
        while (true)
        {
            //expects a 4 byte packet representing a command
            Debug.WriteLine($"Listening for socket command...");
            IBuffer inbuffer = new Windows.Storage.Streams.Buffer(4);
            await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
            int command = BitConverter.ToInt32(inbuffer.ToArray(), 0);
    
            Debug.WriteLine($"Command received: {command}");
            await ParseCommand(command);
        }
    }
    
    private async Task ParseCommand(int command)
    {
        //...
    }
    
    private async Task Listen()
    {
        while (true)
        {
            //expects a 4 byte packet representing a command
            Debug.WriteLine($"Listening for socket command...");
            IBuffer inbuffer = new Windows.Storage.Streams.Buffer(4);
            await _socket.InputStream.ReadAsync(inbuffer, 4, InputStreamOptions.None);
            int command = BitConverter.ToInt32(inbuffer.ToArray(), 0);
    
            Debug.WriteLine($"Command received: {command}");
            await ParseCommand(command);
        }
    }
    
    private async Task ParseCommand(int command)
    {
        //...
    }