问题
I'm currently building a file transfer application with some logging features. What it does is, every time a client connects or disconnects it sends logs (string message) to the server. The logging part is working fine but when I try to send files, the program messes up.
It seems that this is purely a server side issue. What happens is that the previous data; which is the string message for logging, sent from the client seems get stuck on the network stream. When I try to send a file after connecting to the server, I get an error which says, illegal characters in path.
Here's a screenshot of the error.

I believe that this happens because, as you can see on the screenshot above in the FileName
variable, a part of the string ("is connected.") which was sent when the client connected was stuck on the network stream. hello.cpp is the name of the file being sent.
Here's the code.
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
I'm just curious why does this happen. Am I missing something here? Any explanation or suggestion to sort this one out would be highly appreciated. Please enlighten me. :)
回答1:
You are correct in your assumption that after you read the data, the next time you read from the stream, the same data will not be returned again. Your position in the stream is automatically moved forward every time you read from the stream. The problem, though, seems to be that by the time the server reads from the stream, the client has already sent multiple messages (in your example, a log message followed by a file transfer message). When your server application reads the stream, it is not parsing apart the multiple messages and handling them individually. Also, as I mentioned in a comment above, your server application needs to handle the fact that it may receive partial messages as well. It needs to buffer the communication as it comes in and only process the messages once it has received the complete message data.
回答2:
From my experience with network streams, I believe you are right on your assumption of the stream clearing itself out [1].
One other thing I have observed with network streams is that if one or more successive writes are performed, those writes are all put together and will not be cleared until the receiving end reads the entire stream to the end. What this means for you is that it may not be possible to distinguish between messages if more than one message is sent before the recipient reads the stream.
One way around this is to insert placeholders in your stream to mark the start and/or end of distinct messages. If that is done, your program will be able to determine where one chunk of data starts and where it ends. Implementing placeholders, in my opinion, should free you of the trouble you are currently experiencing.
Sample Implementation
Note: Code in italics may be incorrect
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
What this does is that it reads the stream until it reads a chunk of data which ends with the placeholder signature then it stops and does something with the message that has been read from the stream.
Of course in your implementation you could have multiple concatenated messages so you'll have to have a way of preserving bytes from the next message that were 'accidentally' read. You also will have to do a pattern matching of the chunk of data read instead of checking if the beginning/end matches the placeholder.
来源:https://stackoverflow.com/questions/14978965/previous-data-remains-on-the-network-stream