Получение данных из модуля записи двоичных файлов в сетевой поток

Это можно рассматривать как продолжение моей предыдущей темы .

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

Вот скриншот ошибки, которую я получаю.

Как вы можете видеть на скриншоте, переменная FileName содержит несколько значений. Это имя файла, который я отправляю. Если вы заметили, переменная содержит ello.cpp, это должно быть hello.cpp. hello.cpp — это имя отправляемого файла. Конечные символы были содержимым файла. Завершающие символы больше не проблема, пока я могу правильно решить это, имя файла будет единственным, сохраненным в переменной.

Ошибка возникает в процессе чтения сетевого потока. Чего я хочу добиться, так это того, что я хочу предотвратить чтение сетевого потока, если это не сообщение журнала. Мне нужно где-то поставить networkstream.read, чтобы он не занимал первую букву файла. Может какая-то фильтрация? Или, если можно иметь только один код, который может получить сообщение журнала и имя файла, это было бы просто замечательно. Имя файла отправляется с помощью модуля записи двоичных файлов, я пытался использовать сетевой поток для получения имени файла, но это не сработало. Я что-то упустил здесь?

Вот код серверной части.

    Try

        While FileSharingStarted

            If CBool(ClientSocket.Available) Then

                Dim ByteData(ClientSocket.ReceiveBufferSize) As Byte

                'This reading of the stream here causes the problem. That's why the
                'file name isn't read properly. This reading part here is actually
                'intended for the logging part. What happens is that when the string
                'for the logging part is sent by the client side, this is being read
                'using this. But when a file is sent, it is read here in this part
                'that's why when the algorithm for retrieving the file name below
                'kicks in, the first letter of the file name is missing, and ruins
                'the whole thing.
                networkStream.Read(ByteData, 0, CInt(ClientSocket.ReceiveBufferSize))

                fileLogMessage = Encoding.ASCII.GetString(ByteData)
                Dim ConnectionStatus() As String
                ConnectionStatus = fileLogMessage.Split(CChar(" "))

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

                    'Creates a log when the user connects.

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

                   'Creates a log when the user disconnects.

                Else

                    'Algorithm for receiving the files.

                    Dim FileName, FilePath As String
                    Dim FileLength As Long

                    'This part here conflicts with the `networkstream.read` above
                    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

                            If ReadBytes = 0 Then
                                fileStream.Close()
                                FileTransferInterrupted = True
                                Exit Do
                            Else
                                ReadBytes = ClientSocket.GetStream.Read(FileData, 0, FileData.Length())
                                fileStream.Write(FileData, 0, ReadBytes)
                                TotalData += ReadBytes
                            End If

                        Loop

                    End Using

                    If FileTransferInterrupted Then

                        MessageBox.Show("File transfer interrupted.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information)
                        FileSharingStatusBar.Panels.Item(1).Text = "Idle."

                    Else

                        MessageBox.Show("File received.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information)
                        FileSharingStatusBar.Panels.Item(1).Text = "Idle."

                    End If

                End If

            End If

        End While

Вот код для клиентской части.

Try

        Dim fileInfo As New FileInfo(txtFilePath.Text)
        Dim binaryWriter As New BinaryWriter(fileClientSocket.GetStream())

        'Here's the part where it writes the file name on the
        'stream using the binary writer
        binaryWriter.Write(fileInfo.Name)
        binaryWriter.Write(fileInfo.Length)

        Using fileStream As New FileStream(txtFilePath.Text, IO.FileMode.Open, IO.FileAccess.Read)

            Dim FileData(8092) As Byte
            Dim ReadBytes As Integer = -1
            FileSharingStatusBar.Panels.Item(1).Text = "Sending file . . ."

            Do Until ReadBytes = 0

                If CBool(fileClientSocket.Available) Then

                    MessageBox.Show("File transfer interrupted.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information)
                    Exit Sub

                Else

                    ReadBytes = fileStream.Read(FileData, 0, FileData.Length)
                    fileClientSocket.GetStream.Write(FileData, 0, ReadBytes)

                End If

            Loop

        End Using

        txtFilePath.Text = ""
        MessageBox.Show("File sent.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information)
        FileSharingStatusBar.Panels.Item(1).Text = "Idle."

    Catch ex As Exception

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

End Try

Предложения будут высоко оценены.

Заранее спасибо! :)


person Ryklon Zen    schedule 22.02.2013    source источник


Ответы (2)


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

Public Interface IIncomingDataBuffer
    Sub Append(ByVal data() As Byte)
    Event MessageReceived As EventHandler(Of MessageEventArgs)
End Interface

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

person Steven Doggart    schedule 22.02.2013
comment
Спасибо за ваш ответ. Кажется, это неплохая идея. Но дело в том, что я могу сделать это только в том случае, если смогу получить имя файла отправляемых файлов. Пока я не могу получить имя файла в сетевом потоке (поскольку оно записано с помощью двоичного модуля записи), я не могу этого сделать, потому что я не знаю, как получить имя файла с помощью сетевого потока, когда оно записано с помощью бинарный писатель. И, кроме того, мне просто нужна правильная реструктуризация кода, особенно часть networkstream.read, чтобы она не мешала при отправке файла. - person Ryklon Zen; 22.02.2013
comment
Ну, насколько я понимаю, вы отправляете два разных вида сообщений: сообщения журнала и сообщения о передаче файлов. В сообщении журнала у вас есть только одно поле полезной нагрузки: текст для журнала. Но в сообщении о передаче файла у вас есть три поля полезной нагрузки: имя файла, размер файла и содержимое файла. Таким образом, вам нужно не только определить, как вы собираетесь определять, где начинается и заканчивается каждое сообщение, но вам также необходимо определить, как вы будете определять, где начинается и заканчивается каждое поле в сообщении. - person Steven Doggart; 22.02.2013
comment
По сути, вы определяете свой собственный протокол. Это ни в коем случае не невыполнимая задача, но это очень важный шаг в вашем дизайне, который нельзя просто пропустить или приукрасить. Если вы не хотите тратить время на разработку хорошего протокола, я рекомендую вам просто использовать уже существующий протокол, например SOAP. Платформа .NET имеет встроенные классы для работы с SOAP, а Visual Studio IDE даже предоставляет удобные инструменты WCF и ASMX Web Service для разработки как клиентской, так и серверной сторон приложения на основе SOAP. - person Steven Doggart; 22.02.2013
comment
Ok. Прежде чем я начну искать статьи о SOAP, я хотел бы задать еще один вопрос. Нет ли другого способа получить имя файла, которое было записано с помощью двоичного записывающего устройства с использованием сетевого потока? Что-то, что устранило бы использование binaryReader.Readstring в коде моего сервера и использовало бы только networkStream.Read для получения имен файлов и сообщений журнала, которые являются строками. Если это невозможно, то почему это так? Я просто спрашиваю, ради простоты ведения дел. Я очень близок к завершению приложения, если я смогу это осуществить. - person Ryklon Zen; 22.02.2013
comment
РЖУ НЕ МОГУ. Если нет другого пути, я бы взялся за SOAP, как можно скорее. :) - person Ryklon Zen; 22.02.2013
comment
Точнее, что-то вроде: BinaryWriter.Write("Some String") извлечение с помощью NetworkStream.Read или NetworkStream.Write("Some string") извлечение с помощью BinaryReader.Read Возможно ли это? - person Ryklon Zen; 22.02.2013
comment
Вы можете использовать New BinaryReader(New MemoryStream(ByteData)) для создания объекта BinaryReader, который будет считываться из массива байтов после того, как вы прочитаете его из потока. Это помогает? Я не совсем уверен, что вы спрашиваете. - person Steven Doggart; 22.02.2013
comment
Спасибо за ответ. Я попробую ваше предложение позже. Сейчас здесь 3:24. Я должен отдохнуть. РЖУ НЕ МОГУ - person Ryklon Zen; 22.02.2013

Я понял, как это решить. Благодаря одному из предложений моего друга в Facebook, коллеге-программисту. Что я сделал, так это отправил строку, которая сигнализирует серверной части о получении файла. Теперь все хорошо. :)

Я также хотел бы поблагодарить Стивена Доггарта за все его усилия и предложения, особенно в отношении SOAP.

Большое спасибо, сэр! :)

person Ryklon Zen    schedule 23.02.2013