我目前正在构建具有一些日志记录功能的文件传输应用程序。它的作用是,每次客户端连接或断开连接时,它都会将日志(字符串消息)发送到服务器。日志部分工作正常,但是当我尝试发送文件时,程序会搞砸。
这似乎纯粹是服务器方面的问题。会发生什么是以前的数据;这是用于记录的字符串消息,从客户端发送似乎卡在网络流上。当我在连接到服务器后尝试发送文件时,出现错误,上面写着路径中的非法字符。
以下是错误的屏幕截图。
我认为发生这种情况的原因是,正如您在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
我只是好奇为什么会这样。我在这里错过了什么吗?对此进行排序的任何解释或建议都将受到高度赞赏。请赐教。 :)
答案 0 :(得分:1)
您的假设是正确的,在您读取数据后,下次从流中读取数据时,将不会再次返回相同的数据。每次从流中读取时,您在流中的位置都会自动向前移动。但是,问题似乎是当服务器从流中读取时,客户端已经发送了多条消息(在您的示例中,日志消息后跟文件传输消息)。当您的服务器应用程序读取流时,它不会解析多个消息并单独处理它们。另外,正如我在上面的评论中提到的,您的服务器应用程序需要处理它也可能接收部分消息的事实。它需要在通信进入时缓冲通信,并且只在收到完整的消息数据后才处理消息。
答案 1 :(得分: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
这样做是因为它读取流直到它读取以占位符签名结尾的数据块然后停止并对从流中读取的消息执行某些操作。
当然,在您的实现中,您可能会有多个连接消息,因此您必须有一种方法可以保留“意外”读取的下一条消息中的字节。您还必须对读取的数据块进行模式匹配,而不是检查开头/结尾是否与占位符匹配。