Предыдущие данные остаются в сетевом потоке

В настоящее время я создаю приложение для передачи файлов с некоторыми функциями ведения журнала. Что он делает, так это то, что каждый раз, когда клиент подключается или отключается, он отправляет журналы (строковое сообщение) на сервер. Часть ведения журнала работает нормально, но когда я пытаюсь отправить файлы, программа дает сбой.

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

Вот снимок экрана с ошибкой.

введите здесь описание изображения

Я полагаю, что это происходит потому, что, как вы можете видеть на скриншоте выше в переменной FileName, часть строки ("подключено"), которая была отправлена, когда клиент подключился, застрял в сетевом потоке. hello.cpp — это имя отправляемого файла.

Вот код.

Dim ClientSocket As TcpClient = CType(tcpSocket, TcpClient)

Dim networkStream As NetworkStream = ClientSocket.GetStream() 'This stream is
'for the logging part. This part here, I think causes the error because when I
'remove this and the conditions for the logging part, leaving the file sharing
'algorithm alone, the whole program works.

While FileSharingStarted

    If CBool(ClientSocket.Available) Then
        Dim ByteData(ClientSocket.ReceiveBufferSize) As Byte
        networkStream.Read(ByteData, 0, CInt(ClientSocket.ReceiveBufferSize))
        fileLogMessage = Encoding.ASCII.GetString(ByteData)

        If fileLogMessage.Contains("is connected." & Environment.NewLine) Then

            'This block here is for logging purposes. It receives the string 
            'message sent by the client when it connects and does some stuffs.

        ElseIf fileLogMessage.Contains("is disconnected." & Environment.NewLine) Then

            'This block here is for logging purposes again. It receives the 
            'string message sent by the client when it disconnects and then 
            'does some stuffs.

        Else

            'This part is for receiving the file sent by the client.

            Dim FileName, FilePath As String
            Dim FileLength As Long
            Dim binaryReader As New BinaryReader(ClientSocket.GetStream())

            FileName = binaryReader.ReadString()
            FileLength = binaryReader.ReadInt64()
            FilePath = Path.Combine(System.Environment.CurrentDirectory & "\home", FileName)

            Dim FileData(8092) As Byte
            Dim TotalData As Long = 0
            Dim ReadBytes As Integer = -1

            Using FileStream As New FileStream(FilePath, FileMode.Create, FileAccess.Write)
                FileSharingStatusBar.Panels.Item(1).Text = "Receiving file . . ."

                Do Until TotalData = FileLength
                    ReadBytes = ClientSocket.GetStream.Read(FileData, 0, FileData.Length())
                    FileStream.Write(FileData, 0, ReadBytes)
                    TotalData += ReadBytes
                Loop
            End Using

            MessageBox.Show("File received.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information)

            FileSharingStatusBar.Panels.Item(1).Text = "Idle."
        End If
    End If

End While

Мне просто интересно, почему это происходит. Я что-то упустил здесь? Любое объяснение или предложение разобраться с этим будет высоко оценено. Пожалуйста, просветите меня. :)


person Ryklon Zen    schedule 20.02.2013    source источник
comment
Как вы определяете, где начинается и заканчивается каждое сообщение в потоке?   -  person Steven Doggart    schedule 20.02.2013
comment
Что произойдет, если вы удалите networkStream и весь связанный с ним код? Вы можете успешно прочитать весь файл? Я спрашиваю об этом, потому что ваше изображение показывает очень большой размер файла (более гигабайта).   -  person Alex Essilfie    schedule 20.02.2013
comment
@AlexEssilfie Да. Я могу успешно получить файл. Это не реальный размер файла. hello.cpp весит всего 168 Кб. Я думаю, это происходит из-за петли. Это часть проблемы, но я думаю, что она будет решена, когда я смогу успешно справиться с проблемой сетевого потока.   -  person Ryklon Zen    schedule 20.02.2013
comment
@StevenDoggart Ошибаетесь, отправленное сообщение имеет постоянный размер. Простите меня, но я не получил ваш последний комментарий.   -  person Ryklon Zen    schedule 20.02.2013
comment
Итак, когда вы отправляете сообщения журнала, вы дополняете их до той же длины в байтах, что и сообщения о передаче файлов?   -  person Steven Doggart    schedule 20.02.2013
comment
@StevenDoggart Нет, размер сообщений журнала имеет постоянный размер. Постоянные строки, поступающие со стороны клиента. Эти сообщения журнала отправляются только тогда, когда клиент подключается или отключается. При этом отправляемые файлы различаются по размеру.   -  person Ryklon Zen    schedule 20.02.2013
comment
@RyklonZen: ваши сообщения журнала имеют постоянную длину, так что это исправляет одну вещь. Следующий вопрос: как найти имя файла в потоке? Вы «пропускаете» некоторые байты? имена фиксированной длины? как определить, где начинается имя файла и где оно заканчивается?   -  person Alex Essilfie    schedule 20.02.2013
comment
Дело в том, что вы, похоже, исходите из предположения, что когда вы пишете две строки на клиентской стороне сокета, а затем читаете один раз на серверной стороне сокета, вы получите только первую строку, которая была отправлена . Связь через сокет так не работает. Это просто бесшовный поток данных. Когда вы читаете на стороне сервера, вы можете получить 1 сообщение, частичное сообщение или несколько сообщений. Вы должны форматировать свои сообщения таким образом, чтобы вы знали, где они начинаются и заканчиваются, когда вы получаете их на другой стороне.   -  person Steven Doggart    schedule 20.02.2013
comment
@StevenDoggart Вы правы. Возможно, я что-то упустил в сетевом потоке. Я предполагаю, что когда я отправляю строку по сети, после получения этих данных на другой стороне поток очищается или очищается. Разве это не так? Это моя текущая реализация. Пожалуйста, поправьте меня, если я ошибаюсь. Буду рад, если вы поведете меня по правильному пути. :)   -  person Ryklon Zen    schedule 20.02.2013
comment
@AlexEssilfie Я ожидаю, что сетевой поток очистится, когда сервер успешно получит данные, так что, когда клиент отправляет файл, только имя файла клиента будет сохранено в сетевом потоке во время другого прохода внешнего петля. Проблема в том, что на скриншоте этого не происходит. FileName = binaryReader.ReadString() — это код, который я использую для определения имени получаемого файла. Мое текущее понимание состоит в том, что сетевой поток должен очищаться или очищаться после получения данных. Разве это не так? Поправьте меня если я ошибаюсь.   -  person Ryklon Zen    schedule 20.02.2013


Ответы (2)


Вы правы в своем предположении, что после чтения данных при следующем чтении из потока те же данные не будут возвращены снова. Ваша позиция в потоке автоматически перемещается вперед каждый раз, когда вы читаете из потока. Однако проблема, по-видимому, заключается в том, что к тому времени, когда сервер читает поток, клиент уже отправил несколько сообщений (в вашем примере сообщение журнала, за которым следует сообщение о передаче файла). Когда ваше серверное приложение читает поток, оно не анализирует несколько сообщений и не обрабатывает их по отдельности. Кроме того, как я упоминал в комментарии выше, ваше серверное приложение должно обрабатывать тот факт, что оно также может получать частичные сообщения. Он должен буферизовать сообщение по мере его поступления и обрабатывать сообщения только после получения полных данных сообщения.

person Steven Doggart    schedule 20.02.2013
comment
Да. Спасибо за ваши ответы, я должен проверить свой код и посмотреть, что с ним не так, следуя как предложениям @Alex Esselfie, так и вашим. Спасибо за время и терпение. :) - person Ryklon Zen; 20.02.2013

Исходя из моего опыта работы с сетевыми потоками, я считаю, что вы правы в своем предположении, что поток самоочищается [1].

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

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

Пример реализации
Примечание. Код, выделенный курсивом, может быть неправильным

Dim placeholder As Byte() = New Byte() {&H00, &H01, &HFE, &HFF}
Dim message As New List(Of Byte)()
Dim data As Byte()

Do While True
    data = stream.ReadBytes(1024)
    If data.Skip(data.Length - placeholder.Length).SequenceEquals(placeholder) Then
        message.AddRange(data.Take(data.Length - placeholder.Length)
        Exit Do
    Else
        message.AddRange(data)
    End If
Loop

' do something with the message read

Что это делает, так это то, что он читает поток до тех пор, пока не прочитает фрагмент данных, который заканчивается сигнатурой-заполнителем, затем он останавливается и что-то делает с сообщением, которое было прочитано из потока.

Конечно, в вашей реализации у вас может быть несколько объединенных сообщений, поэтому вам понадобится способ сохранения байтов из следующего сообщения, которое было «случайно» прочитано. Вам также придется выполнять сопоставление с образцом прочитанного фрагмента данных вместо проверки того, соответствует ли начало/конец заполнителю.

person Alex Essilfie    schedule 20.02.2013
comment
Хорошо, я понимаю вашу точку зрения. Мне просто интересно, почему это происходит. Я имею в виду, что я просто отправляю постоянную строку, Ryklon подключен, вот так. Я предполагаю, что они рассматриваются индивидуально, как сказал @Steven Doggart. Я попытался прочитать количество отправленных байтов и точное количество байтов отправленной строки, за исключением символа новой строки, после еще одного прохода другого цикла байты не прочитаны. Может быть, клиент отправляет пробелы? Я действительно не знаю, это другая теория. Я думаю, я должен проверить код дальше. В любом случае, спасибо за ваше терпение и время. :) - person Ryklon Zen; 20.02.2013