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

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

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

Вот скриншот ошибки.

Я считаю, что это происходит потому, что, как вы можете видеть на скриншоте выше в 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

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

2 ответа

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

Исходя из моего опыта работы с сетевыми потоками, я полагаю, что вы правы, полагая, что поток очищается сам [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

Это означает, что он читает поток до тех пор, пока не прочитает порцию данных, которая заканчивается подписью-заполнителем, затем останавливается и что-то делает с сообщением, прочитанным из потока.

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

Другие вопросы по тегам