I have a Client - Server solution that uses TCP to communicate. Everything was just fine until some client try to connect throe VPN. After debugging a similar condition I have notice that Client doesn’t get all sended bytes. For example:
Sending (packet index – packet number – first byte in packet):
1 – 1 – 1
2 – 2 – 2
3 – 3 – 3
4 – 4 – 4
Receiving:
1 – 1 – 1
2 – 2 – 2
3 – 14463453452123 – 0
4 – 3 – 3
5 – 4 – 4
My question is: how can I remove this garbage packages? And where they come from?
Here a simple code for sending and receiving bytes:
Sending Function:
Public Function SendMessage(ByVal Client As TcpClient, ByVal Type As MessagesType, Optional ByVal Message As Object = "", Optional ByVal ID As ULong = 0) As Boolean
If Client.Connected Then
Dim networkStream As NetworkStream = Client.GetStream()
Try
Dim Start As Date = Now
Do
If networkStream.CanWrite Then
Dim sendBytes As [Byte]()
If Type = MessagesType.SendFile And TypeOf Message Is FileNewSending Then
Dim mess As FileNewSending = Message
Dim fs As New FileStream(mess.FullPath, FileMode.Open)
Dim objReader As New BinaryReader(fs)
Dim sendFile As [Byte]() = objReader.ReadBytes(fs.Length)
fs.Close()
sendBytes = ObjectToByteArray(New TCPData(Type, New FileSending(mess.TargetDir, mess.Name, System.IO.Path.GetExtension(mess.FullPath), sendFile), ID))
Else
sendBytes = ObjectToByteArray(New TCPData(Type, Message, ID))
End If
Dim writer As New BinaryWriter(networkStream, Encoding.BigEndianUnicode)
Dim size As UInt64 = sendBytes.Length
writer.Write(size)
Dim packetIndex As UInt64 = 0
For i As Integer = 0 To sendBytes.Length - 1 Step Client.SendBufferSize
writer.Write(packetIndex)
If size < i + Client.SendBufferSize Then
writer.Write(sendBytes, i, size - i)
Else
writer.Write(sendBytes, i, Client.SendBufferSize)
End If
packetIndex += 1
Next i
writer.Flush()
Return True
End If
Loop While (Now - Start).TotalSeconds < WaitForResponceTimeOut
Return False
Catch ex As IOException
Return False
Catch ex As Exception
Log("Network Error", ex.ToString)
Return False
End Try
End If
Return False
End Function
Receiving function:
Public Function ReciveMessage(ByVal Client As TcpClient, ByRef Type As MessagesType, ByRef Message As Object, ByRef ID As ULong) As Boolean
If Client.Connected Then
Try
Dim networkStream As NetworkStream = Client.GetStream()
If networkStream.DataAvailable Then
Dim bytes() As Byte = Nothing
Dim ReciveBufferLength As UInt64 = 0
Dim TotalReciaveLength As UInt64 = 0
Dim readStarted As Date = Now
Dim lIndex As UInt64 = 0
Dim packetIndex As UInt64 = 0
Do
If networkStream.DataAvailable Then
readStarted = Now
Dim reader As New BinaryReader(networkStream, Encoding.BigEndianUnicode)
If TotalReciaveLength = 0 Then
ReciveBufferLength = reader.ReadUInt64()
lIndex = reader.ReadUInt64()
ReDim bytes(ReciveBufferLength - 1)
If ReciveBufferLength > Client.ReceiveBufferSize Then
TotalReciaveLength = Client.ReceiveBufferSize
Else
TotalReciaveLength = ReciveBufferLength
End If
reader.Read(bytes, 0, TotalReciaveLength)
packetIndex += 1
Else
lIndex = reader.ReadUInt64()
If packetIndex = lIndex Then
If ReciveBufferLength > TotalReciaveLength + Client.ReceiveBufferSize Then
reader.Read(bytes, TotalReciaveLength, Client.ReceiveBufferSize)
TotalReciaveLength += Client.ReceiveBufferSize
Else
reader.Read(bytes, TotalReciaveLength, ReciveBufferLength - TotalReciaveLength)
TotalReciaveLength = ReciveBufferLength
End If
packetIndex += 1
End If
End If
Else
System.Threading.Thread.Sleep(100)
End If
Loop While (networkStream.DataAvailable And ReciveBufferLength = 0) Or (ReciveBufferLength > 0 And TotalReciaveLength < ReciveBufferLength And (Now - readStarted).Seconds < WaitForReciaveTimeOut)
Dim msg As TCPData = ByteArrayToObject(bytes)
If msg Is Nothing Then
Return False
Else
Type = msg.Type
Message = msg.Data
ID = msg.ID
Return True
End If
End If
Catch ex As OverflowException
Return False
Catch ex As Exception
Log("Network Error", ex.ToString)
Return False
End Try
End If
Return False
End Function
Client initialization:
Client = New TcpClient(ConnectIP, ListnerPort)
Client.SendBufferSize = 1024 * 2
Client.ReceiveBufferSize = 1024 * 2
Client.NoDelay = False
To be clear - I use low level communication. "Sending" function convert all "messages" to byte array thrue:
ObjectToByteArray(New TCPData(Type, Message, ID))
TCPData is a simple serializable class:
<Serializable()> Public Class TCPData
Public Property ID As ULong
Public Property Type As Integer
Public Property Data As Object
Sub New()
End Sub
Sub New(ByVal _Type As Integer, ByVal _Data As Object)
Type = _Type
Data = _Data
ID = 0
End Sub
Sub New(ByVal _Type As Integer, ByVal _Data As Object, ByVal _ID As ULong)
ID = _ID
Type = _Type
Data = _Data
End Sub
End Class
And function "ObjectToByteArray":
Public Function ObjectToByteArray(ByVal _Object As Object, Optional ByVal Compression As Boolean = True) As Byte()
Try
Dim _MemoryStream As New MemoryStream()
Dim _BinaryFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
_BinaryFormatter.AssemblyFormat = Formatters.FormatterAssemblyStyle.Simple
_BinaryFormatter.Serialize(_MemoryStream, _Object)
If UseCompress And Compression Then
Return Compress(_MemoryStream.ToArray())
Else
Return _MemoryStream.ToArray()
End If
Catch _Exception As Exception
Console.WriteLine("Exception caught in process: {0}", _Exception.ToString())
End Try
Return Nothing
End Function
Compress function just "zip" a byte array:
Private Function Compress(ByVal Data() As Byte) As Byte()
Dim msCompressed As New MemoryStream()
Dim size As UInt64 = Data.Length
Dim writer As New BinaryWriter(msCompressed, System.Text.Encoding.ASCII)
writer.Write(size)
Dim zosCompressed As New GZipOutputStream(msCompressed)
zosCompressed.Write(Data, 0, Data.Length)
zosCompressed.Close()
Return msCompressed.ToArray()
End Function
At the receiving end the process is reversed.
I have tried many ways to send bytes:
1. Write whole byte array
writer.Write(sendBytes, 0, sendBytes.length)
2. Tried to adjust SendBufferSize/ClientBufferSize
But nothing helped me.
I look forward to your further advice.