[英]Why am I losing bytes from my Socket InputStream?
I'm creating a socket based system for communicating between desktop and mobile devices.我正在创建一个基于套接字的系统,用于桌面和移动设备之间的通信。 I'm using a simple protocol for reading and writing data to the streams between devices which ultimately end up as bytes:
我使用一个简单的协议来读取和写入数据到设备之间的流,最终以字节结束:
I'm able to successfully strip off the first 4 bytes and resolve the command ok, then strip off the next 4 bytes and resolve the length correctly.我能够成功剥离前 4 个字节并解析命令确定,然后剥离接下来的 4 个字节并正确解析长度。 The problem comes when I strip off the remaining bytes, some of them are missing, and they're missing from the front of the remaining data.
当我去掉剩余的字节时,问题就出现了,其中一些丢失了,并且它们从剩余数据的前面丢失了。
For example;例如; if the command is 1, and the length is 50, then there should be 50 bytes left in the stream but there's only 46 and it's bytes 0-3 which are missing.
如果命令为 1,长度为 50,那么流中应该还有 50 个字节,但只有 46 个字节,并且缺少 0-3 个字节。
The starting data is as follows:起始数据如下:
After converting this to a byte array, I get:将其转换为字节数组后,我得到:
"\\\0\\0\\02\\0\\0\\0C:\\Users\\dave\\Music\\Offaiah-Trouble_(Club_Mix).mp3" "\\\0\\0\\02\\0\\0\\0C:\\Users\\dave\\Music\\Offaiah-Trouble_(Club_Mix).mp3"
(Question - why does the first integer get \\u000 in front of it and the second does not?) (问题 - 为什么第一个整数在它前面得到 \\u000 而第二个没有?)
Here's some snippets of the code I'm using to parse this:这是我用来解析它的一些代码片段:
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: "\\\0\\0\\0", and the BitConverter resolves this to 1此时的 inbuffer 包含:“\\\0\\0\\0”,BitConverter 将其解析为 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 现在包含:“2\\0\\0\\0”,BitConverter 将其解析为“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());
The inbuffer now contains: "sers\\dave\\Music\\Offaiah-Trouble_(Club_Mix).mp3" inbuffer 现在包含:“sers\\dave\\Music\\Offaiah-Trouble_(Club_Mix).mp3”
Where did the missing "C:\\U" go from the front of this?前面缺少的“C:\\U”去哪儿了?
So, I realised that I kept getting down-voted because the question wasn't concise and reproducible.所以,我意识到我一直被否决,因为这个问题不够简洁和可重复。 So I created a small project to demonstrate just this part of the problem, and ironically enough the problem went away.
所以我创建了一个小项目来演示这部分问题,具有讽刺意味的是,问题消失了。
Here's the code:这是代码:
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());
}
}
}
If you create a new blank Universal project and run this, you'll see it produces the correct output.如果您创建一个新的空白 Universal 项目并运行它,您将看到它产生正确的输出。
Eventually I worked out that I had race conditions between this stream reader and another one.最终我发现我在这个流阅读器和另一个流阅读器之间存在竞争条件。 In the main project, I don't read all bytes in one go.
在主项目中,我不会一口气读取所有字节。 I have a separate method reading and parsing the "command" before passing control off to another method to redirect control to one of several worker methods to service that particular command - stripping off the length and then the rest of the payload thereafter.
我有一个单独的方法在将控制权传递给另一种方法之前读取和解析“命令”,以将控制权重定向到几种工作方法之一来为该特定命令提供服务 - 剥离长度,然后剥离其余的有效载荷。
The problem was that the 'command reader' was then reading another 4 bytes from the stream, before the worker could read off it's payload.问题是“命令阅读器”然后从流中读取另外 4 个字节,然后工作人员可以读取它的有效负载。
Obviously the answer is that it should pause here and wait for the worker to finish, but I'm using async and await throughout so it surprised me that I was having this problem.显然答案是它应该在这里暂停并等待工作人员完成,但我一直在使用 async 和 await 所以我遇到这个问题让我感到惊讶。 The cause was a missing await, and the dreaded async void, as follows..
原因是缺少等待,以及可怕的异步无效,如下..
The offending code:违规代码:
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)
{
//...
}
...and the ammended version: ...以及修改后的版本:
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)
{
//...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.