1

我目前正在构建一个具有一些日志记录功能的文件传输应用程序。它的作用是,每次客户端连接或断开连接时,它都会向服务器发送日志(字符串消息)。日志记录部分工作正常,但是当我尝试发送文件时,程序会出错。

看来这纯粹是服务器端问题。发生的是以前的数据;这是用于记录的字符串消息,从客户端发送似乎卡在网络流上。当我在连接到服务器后尝试发送文件时,我收到一条错误消息,指出path 中的非法字符

这是错误的屏幕截图。

在此处输入图像描述

我相信发生这种情况是因为,正如您在上面的屏幕截图中的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

我只是好奇为什么会这样。我在这里错过了什么吗?任何解释或建议来解决这个问题都将受到高度赞赏。请赐教。:)

4

2 回答 2

1

您的假设是正确的,即在您读取数据后,下次您从流中读取时,将不会再次返回相同的数据。每次从流中读取时,您在流中的位置都会自动向前移动。但是,问题似乎在于,当服务器从流中读取数据时,客户端已经发送了多条消息(在您的示例中,一条日志消息后跟一条文件传输消息)。当您的服务器应用程序读取流时,它不会解析多条消息并单独处理它们。此外,正如我在上面的评论中提到的,您的服务器应用程序需要处理它也可能接收部分消息的事实。它需要在通信进入时对其进行缓冲,并且仅在收到完整的消息数据后才处理消息。

于 2013-02-20T17:12:36.930 回答
1

根据我对网络流的经验,我相信您对流清除自身的假设是正确的[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

这样做是它读取流,直到它读取以占位符签名结尾的数据块,然后它停止并对从流中读取的消息执行某些操作。

当然,在您的实现中,您可以有多个连接的消息,因此您必须有一种方法来保存下一条“意外”读取的消息中的字节。您还必须对读取的数据块进行模式匹配,而不是检查开头/结尾是否与占位符匹配。

于 2013-02-20T17:15:32.580 回答