Почему я теряю байты из моего Socket InputStream?

Я создаю систему на основе сокетов для связи между настольными и мобильными устройствами. Я использую простой протокол для чтения и записи данных в потоки между устройствами, которые в конечном итоге заканчиваются байтами:

  1. Первые 4 байта представляют целое число, которое определяет тип команды, которая будет выполнена
  2. Следующие 4 байта представляют собой целое число, которое является длиной остальных байтов в потоке
  3. Остальные байты представляют данные полезной нагрузки, общее количество которых соответствует результату (2.)

Я могу успешно удалить первые 4 байта и разрешить команду ok, затем удалить следующие 4 байта и правильно определить длину. Проблема возникает, когда я удаляю оставшиеся байты, некоторые из них отсутствуют, и они отсутствуют в передней части оставшихся данных.

Например; если команда равна 1, а длина равна 50, то в потоке должно быть 50 байтов, но осталось только 46 и отсутствуют байты 0-3.

Исходные данные следующие:

  • команда: 1
  • длина: 50
  • Полезная нагрузка: C:\Users\dave\Music\Offaiah-Trouble_(Club_Mix).mp3

После преобразования этого в байтовый массив я получаю:

"\ U0001\0\0\02\0\0\0C:\Users\ Дейва \ Music \ Offaiah-Trouble_ (Club_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);

На этом этапе входной буфер содержит: "\u0001\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);

Входящий буфер теперь содержит: "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());

Входящий буфер теперь содержит: "sers\dave\Music\Offaiah-Trouble_(Club_Mix).mp3"

Куда пропал пропавший "C:\U"?

1 ответ

Решение

Итак, я поняла, что меня все время опускают, потому что вопрос не был кратким и воспроизводимым. Поэтому я создал небольшой проект, чтобы продемонстрировать только эту часть проблемы, и, как ни странно, проблема ушла.

Вот код:

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 байта из потока, прежде чем рабочий мог считать его полезную нагрузку.

Очевидно, что ответ заключается в том, что он должен сделать паузу здесь и дождаться завершения рабочего процесса, но я использую асинхронный режим и все время жду, поэтому меня удивило, что у меня возникла эта проблема. Причиной было отсутствие ожидания и страшная асинхронная пустота, как изложено ниже.

Оскорбительный код:

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)
{
    //...
}
Другие вопросы по тегам