Imports System.Threading
Imports System.Net
Imports System.Net.Sockets
Imports System.IO

Public Class tcpCommClient
    Public errMsg As String

    ' Define the delegate type
    Public Delegate Sub ClientCallbackDelegate(ByVal bytes() As Byte, ByVal dataChannel As Integer)
    ' Create Delegate pointer
    Public ClientCallbackObject As ClientCallbackDelegate
    ' Monitor CPU usage
    'Private CPUutil As CpuMonitor

    Private continue_running As Boolean = False
    Private bytes() As Byte
    Private blockSize As UInt16
    Private IP As System.Net.IPAddress
    Private Port As Integer
    Private localAddr As IPAddress
    Private Client As TcpClient
    Private Stream As NetworkStream
    Private fileWriter As clsAsyncUnbuffWriter
    Private fileReader As FileStream
    Private FileBeingSentPath As String
    Private weHaveThePuck As Boolean = False
    Private isRunning As Boolean = False
    Private UserBytesToBeSentAvailable As Boolean = False
    Private UserBytesToBeSent As New MemoryStream
    Private UserOutputChannel As Byte
    Private SystemBytesToBeSentAvailable As Boolean = False
    Private SystemBytesToBeSent() As Byte
    Private SystemOutputChannel As Byte
    Private SendingFile As Boolean = False
    Private ReceivingFile As Boolean = False
    Private IncomingFileName As String
    Private IncomingFileSize As Int64 = 0
    Private outgoingFileSize As UInt64 = 0
    Private outgoingFileName As String
    Private fileBytesRecieved As Int64 = 0
    Private filebytesSent As Int64 = 0
    Private bytesSentThisSecond As Int32 = 0
    Private bytesReceivedThisSecond As Int32 = 0
    Private mbpsOneSecondAverage() As Int32
    Private ReceivedFilesFolder As String = Application.StartupPath & "\downloads"
    Private userName As String
    Private password As String

    Private mbpsSyncObject As New AutoResetEvent(False)

    Private Function StrToByteArray(ByVal text As String) As Byte()
        Dim encoding As New System.Text.UTF8Encoding()
        StrToByteArray = encoding.GetBytes(text)
    End Function

    Private Function BytesToString(ByVal data() As Byte) As String
        Dim enc As New System.Text.UTF8Encoding()
        BytesToString = enc.GetString(data)
    End Function

    Public Function isClientRunning() As Boolean
        Return isRunning
    End Function

    Public Function SetReceivedFilesFolder(ByVal _path As String) As Boolean
        ReceivedFilesFolder = _path
    End Function

    Public Function GetIncomingFileName() As String
        Return IncomingFileName
    End Function

    Public Function GetOutgoingFileName() As String
        Return outgoingFileName
    End Function

    Public Function GetPercentOfFileReceived() As UInt16
        If ReceivingFile Then
            Return CUShort((fileBytesRecieved / IncomingFileSize) * 100)
        Else
            Return 0
        End If
    End Function

    Public Function GetPercentOfFileSent() As UInt16
        If SendingFile Then
            Return CUShort((filebytesSent / outgoingFileSize) * 100)
        Else
            Return 0
        End If
    End Function

    Public Function GetMbps() As String
        Dim currentMbps As Decimal = CalculateMbps(True)
        If currentMbps > 1000000 Then
            Return (currentMbps / 1000000).ToString("N2") & " Mbps"
        Else
            Return (currentMbps / 1000).ToString("N2") & " Kbps"
        End If
    End Function

    Public Function GetLocalIpAddress() As System.Net.IPAddress
        Dim strHostName As String
        Dim addresses() As System.Net.IPAddress

        strHostName = System.Net.Dns.GetHostName()
        addresses = System.Net.Dns.GetHostAddresses(strHostName)

        ' Find an IpV4 address
        For Each address As System.Net.IPAddress In addresses
            If address.ToString.Contains(".") Then
                Return address
            End If
        Next

        ' No IpV4 address? Return the loopback address.
        Return System.Net.IPAddress.Loopback
    End Function

    Public Sub New(ByRef callbackMethod As ClientCallbackDelegate)
        blockSize = 10000

        ' Initialize the delegate variable to point to the user's callback method.
        ClientCallbackObject = callbackMethod
    End Sub

    Public Sub Connect(ByVal IP_Address As String, ByVal prt As Integer)
        Port = prt
        IP = System.Net.IPAddress.Parse(IP_Address)
        continue_running = True

        Dim clientCommunicationThread As New Thread(AddressOf Run)
        clientCommunicationThread.Name = "ClientCommunication"
        clientCommunicationThread.Start()
    End Sub

    Public Sub StopRunning()
        continue_running = False
    End Sub

    Public Function GetBlocksize() As UInt16
        Return blockSize
    End Function

    Public Function GetFile(ByVal _path As String) As Boolean

        Do
            If Not UserBytesToBeSentAvailable Then
                SyncLock UserBytesToBeSent
                    UserBytesToBeSent.Close()
                    UserBytesToBeSent = Nothing
                    UserBytesToBeSent = New MemoryStream(StrToByteArray("GFR:" & _path))
                    UserOutputChannel = 254 ' Text messages / commands on channel 254
                    UserBytesToBeSentAvailable = True
                End SyncLock
                Exit Do
            End If

            If theClientIsStopping() Then Exit Function
            Application.DoEvents()
        Loop

    End Function

    Public Function SendFile(ByVal _path As String) As Boolean

        Do
            If Not UserBytesToBeSentAvailable Then
                SyncLock UserBytesToBeSent
                    UserBytesToBeSent.Close()
                    UserBytesToBeSent = Nothing
                    UserBytesToBeSent = New MemoryStream(StrToByteArray("SFR:" & _path))
                    UserOutputChannel = 254 ' Text messages / commands on channel 254
                    UserBytesToBeSentAvailable = True
                End SyncLock
                Exit Do
            End If

            If theClientIsStopping() Then Exit Function
            Application.DoEvents()
        Loop

    End Function

    Public Sub CancelIncomingFileTransfer()
        Do
            If Not UserBytesToBeSentAvailable Then
                SyncLock UserBytesToBeSent
                    UserBytesToBeSent.Close()
                    UserBytesToBeSent = Nothing
                    UserBytesToBeSent = New MemoryStream(StrToByteArray("Abort->"))
                    UserOutputChannel = 254
                    UserBytesToBeSentAvailable = True
                End SyncLock
                Exit Do
            End If

            If theClientIsStopping() Then Exit Sub
            Application.DoEvents()
        Loop

        FinishReceivingTheFile()
        Try
            File.Delete(ReceivedFilesFolder & "\" & IncomingFileName)
        Catch ex As Exception
        End Try
    End Sub

    Public Sub CancelOutgoingFileTransfer()
        Do
            If Not UserBytesToBeSentAvailable Then
                SyncLock UserBytesToBeSent
                    UserBytesToBeSent.Close()
                    UserBytesToBeSent = Nothing
                    UserBytesToBeSent = New MemoryStream(StrToByteArray("Abort<-"))
                    UserOutputChannel = 254
                    UserBytesToBeSentAvailable = True
                End SyncLock
                Exit Do
            End If

            If theClientIsStopping() Then Exit Sub
            Application.DoEvents()
        Loop

        StopSendingTheFile()
    End Sub

    Public Function SendBytes(ByVal bytes() As Byte, Optional ByVal channel As Byte = 1) As Boolean

        If channel = 0 Or channel > 250 Then
            MsgBox("Data can not be sent using channel numbers less then 1 or greater then 250.", MsgBoxStyle.Critical, "TCP_Client")
            Exit Function
        End If

        Do
            If Not UserBytesToBeSentAvailable Then
                SyncLock UserBytesToBeSent
                    UserBytesToBeSent.Close()
                    UserBytesToBeSent = Nothing
                    UserBytesToBeSent = New MemoryStream(bytes)
                    UserOutputChannel = channel
                    UserBytesToBeSentAvailable = True
                End SyncLock
                Exit Do
            End If

            If theClientIsStopping() Then Exit Function
            Application.DoEvents()
        Loop

    End Function

    Private Function RcvBytes(ByVal data() As Byte, Optional ByVal dataChannel As Integer = 1) As Boolean
        ' dataType: >0 = data channel, 251 and up = internal messages. 0 is an invalid channel number (it's the puck)

        If dataChannel < 1 Then
            RcvBytes = False
            Exit Function
        End If

        Try
            ClientCallbackObject(data, dataChannel)
        Catch ex As Exception
            RcvBytes = False

            ' An unexpected error.
            Debug.WriteLine("Unexpected error in Client\RcvBytes: " & ex.Message)
        End Try

    End Function

    Private Function CreateFolders(ByVal _path As String) As Boolean
        CreateFolders = True

        Dim parts() As String
        Dim path As String = ""
        Dim count As Int32
        parts = Split(_path, "\")


        path = parts(0)
        For count = 1 To parts.Length - 2
            path += "\" & parts(count)
            Try
                If Not Directory.Exists(path) Then
                    Directory.CreateDirectory(path)
                End If
            Catch ex As Exception
            End Try
        Next

    End Function

    Private Function SendExternalSystemMessage(ByVal message As String) As Boolean

        SystemBytesToBeSent = StrToByteArray(message)
        SystemOutputChannel = 254 ' Text messages / commands on channel 254
        SystemBytesToBeSentAvailable = True

    End Function

    Private Function BeginToReceiveAFile(ByVal _path As String) As Boolean
        Dim readBuffer As Int32 = 0
        ReceivingFile = True
        BeginToReceiveAFile = True
        fileBytesRecieved = 0

        Try
            CreateFolders(_path)
            fileWriter = New clsAsyncUnbuffWriter(_path, True,
                            1024 * (clsAsyncUnbuffWriter.GetPageSize()), IncomingFileSize)

        Catch ex As Exception
            _path = ex.Message
            ReceivingFile = False
        End Try

        If Not ReceivingFile Then
            Try
                fileWriter.Close()
            Catch ex As Exception
            End Try
            Return False
        End If
    End Function

    Private Function HandleIncomingFileBytes(ByRef bytes() As Byte) As Boolean

        Try
            fileWriter.Write(bytes, bytes.Length)
            HandleIncomingFileBytes = True
        Catch ex As Exception
            HandleIncomingFileBytes = False
        End Try

    End Function

    Private Sub FinishReceivingTheFile()
        Try
            fileWriter.Close()
            fileWriter = Nothing
            ReceivingFile = False
        Catch ex As Exception
            ReceivingFile = False
        End Try
    End Sub

    Private Sub StopSendingTheFile()
        Try
            SendingFile = False
            fileReader.Close()
            fileReader = Nothing
            GC.GetTotalMemory(True)
        Catch ex As Exception
            SendingFile = False
            GC.GetTotalMemory(True)
        End Try
    End Sub

    Private Sub WrapUpIncomingFile()

        If ReceivingFile Then
            Try
                fileWriter.Close()
                fileWriter = Nothing
                GC.GetTotalMemory(True)
            Catch ex As Exception
            End Try

            Try
                File.Delete(ReceivedFilesFolder & "\" & IncomingFileName)
            Catch ex As Exception
            End Try
        End If
    End Sub

    Private Function CheckSessionPermissions(ByVal cmd As String) As Boolean
        ' Your security code here...

        Return True
    End Function

    Private Function BeginFileSend(ByVal _path As String, ByVal fileLength As Long) As Boolean
        filebytesSent = 0

        Try

            fileReader = New FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.None, clsAsyncUnbuffWriter.GetPageSize)
            SendingFile = True
            BeginFileSend = True

        Catch ex As Exception
            BeginFileSend = False
            _path = ex.Message
            SendingFile = False
        End Try

        Try
            If Not BeginFileSend Then fileReader.Close()
        Catch ex As Exception
        End Try

    End Function

    Private Sub GetMoreFileBytesIfAvailable()
        Dim bytesRead As Integer

        If SendingFile And Not SystemBytesToBeSentAvailable Then
            Try
                If SystemBytesToBeSent.Length <> blockSize Then ReDim SystemBytesToBeSent(blockSize - 1)
                bytesRead = fileReader.Read(SystemBytesToBeSent, 0, blockSize)
                If bytesRead <> blockSize Then ReDim Preserve SystemBytesToBeSent(bytesRead - 1)

                If bytesRead > 0 Then
                    SystemOutputChannel = 252 ' File transfer from client to server
                    SystemBytesToBeSentAvailable = True
                    filebytesSent += bytesRead
                Else

                    ReDim SystemBytesToBeSent(blockSize - 1)
                    SendExternalSystemMessage("<-Done") ' Send the server a completion notice.
                    SystemMessage("<-Done")
                    SendingFile = False

                    ' Clean up
                    fileReader.Close()
                    fileReader = Nothing
                    GC.GetTotalMemory(True)
                End If
            Catch ex As Exception
                SendExternalSystemMessage("ERR: " & ex.Message)

                ' We're finished.
                ReDim SystemBytesToBeSent(blockSize - 1)
                SendingFile = False
                fileReader.Close()
            End Try
        End If

    End Sub

    Private Function GetFilenameFromPath(ByVal filePath As String) As String
        Dim filePathParts() As String

        If filePath.Trim = "" Then Return ""

        filePathParts = Split(filePath, "\")
        GetFilenameFromPath = filePathParts(filePathParts.Length - 1)
    End Function

    Private Sub HandleIncomingSystemMessages(ByVal bytes() As Byte, ByVal channel As Int32)

        If channel = 254 Then ' Text commands / messages passed between server and client
            Dim message As String = BytesToString(bytes)
            Dim tmp As String = ""
            Dim filePath As String

            ' Get File Request: The client wants us to send them a file.
            If message.Length > 4 Then tmp = message.Substring(0, 4)
            If tmp = "GFR:" Then ' Get File Request
                ' Get file path...
                filePath = message.Substring(4, message.Length - 4)

                ' Does it exist?
                If File.Exists(message.Substring(4, message.Length - 4)) Then
                    ' Are we already busy sending them a file?
                    If Not SendingFile Then
                        Dim _theFilesInfo As New FileInfo(filePath)
                        outgoingFileName = GetFilenameFromPath(filePath)
                        outgoingFileSize = CULng(_theFilesInfo.Length)
                        If BeginFileSend(filePath, _theFilesInfo.Length) Then
                            ' Send only the file NAME. It will have a different path on the other side.
                            SendExternalSystemMessage("Sending:" & outgoingFileName &
                                                      ":" & outgoingFileSize.ToString)
                            SystemMessage("Sending file:" & outgoingFileName)
                        Else
                            ' FilePath contains the error message.
                            SendExternalSystemMessage("ERR: " & filePath)
                        End If
                    Else
                        ' There's already a GFR in progress.
                        SendExternalSystemMessage("ERR: File: ''" &
                                                  FileBeingSentPath &
                                                  "'' is still in progress. Only one file " &
                                                  "may be transfered (from client to server) at a time.")
                    End If
                Else
                    ' File doesn't exist. Send an error.
                    SendExternalSystemMessage("ERR: The requested file can not be found by the server.")
                End If
            End If

            If message.Length > 7 Then tmp = message.Substring(0, 8)
            If tmp = "Sending:" Then
                ' Strip away the headder...
                Dim msgParts() As String = Split(message, ":")
                IncomingFileSize = Convert.ToInt64(msgParts(2))
                IncomingFileName = msgParts(1)
                tmp = ReceivedFilesFolder & "\" & IncomingFileName
                SystemMessage("Receiving file: " & IncomingFileName)
                If Not BeginToReceiveAFile(tmp) Then
                    SystemMessage("ERR: " & tmp)
                    SendExternalSystemMessage("Abort<-")
                End If
            End If

            If message.Length > 10 Then tmp = message.Substring(0, 10)
            If tmp = "blocksize:" Then
                Dim msgParts() As String = Split(message, ":")
                blockSize = Convert.ToUInt16(msgParts(1))
            End If

            If message = "->Done" Then
                FinishReceivingTheFile()
                SystemMessage("->Done")
            End If

            ' We've been notified that no file data will be forthcoming.
            If message = "Abort->" Then
                FinishReceivingTheFile()
                SystemMessage("->Aborted.")
                Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal
                Try
                    File.Delete(ReceivedFilesFolder & "\" & IncomingFileName)
                Catch ex As Exception
                End Try
            End If

            ' Notification that the server has complied with our  
            ' request to stop sending bytes for this 
            ' (server->client) file transfer.
            If message = "->Aborted." Then
                SystemMessage("->Aborted.")
                WrapUpIncomingFile()
            End If

            ' Notification that the server has complied with our  
            ' request to stop recieving bytes for this 
            ' (client->server) file transfer.
            If message = "<-Aborted." Then
                SystemMessage("<-Aborted.")
            End If

            If message.Length > 4 Then tmp = message.Substring(0, 4)
            If tmp = "ERR:" Then ' The server has sent us an error message.
                ' Pass it on up to the user.
                SystemMessage(message)
            End If

        ElseIf channel = 253 Then ' File transfer from server to client
            Try
                If ReceivingFile Then
                    HandleIncomingFileBytes(bytes)
                    fileBytesRecieved += bytes.LongLength
                End If
            Catch ex As Exception
            End Try
        ElseIf channel = 252 Then ' File transfer from client to server

        ElseIf channel = 251 Then ' reserved.

        End If
    End Sub

    Private Function HandleOutgoingInternalSystemMessage() As Boolean
        Dim tmp(1) As Byte
        HandleOutgoingInternalSystemMessage = False
        Dim _size As Integer

        GetMoreFileBytesIfAvailable()

        ' Handle outgoing system stuff here
        If SystemBytesToBeSentAvailable = True Then
            HandleOutgoingInternalSystemMessage = True
            If SystemBytesToBeSent.Length > blockSize Then
                ' Send Channel
                tmp(0) = SystemOutputChannel
                Stream.Write(tmp, 0, 1)
                bytesSentThisSecond += 1

                ' Send packet size
                _size = blockSize
                tmp = BitConverter.GetBytes(_size)
                Stream.Write(tmp, 0, 2)
                bytesSentThisSecond += 2

                ' Send packet
                Stream.Write(GetSome(SystemBytesToBeSent, blockSize, SystemBytesToBeSentAvailable), 0, _size)
                bytesSentThisSecond += _size
            Else
                ' Send Channel
                tmp(0) = SystemOutputChannel
                Stream.Write(tmp, 0, 1)
                bytesSentThisSecond += 1

                ' Send packet size
                _size = SystemBytesToBeSent.Length
                tmp = BitConverter.GetBytes(_size)
                Stream.Write(tmp, 0, 2)
                bytesSentThisSecond += 2

                ' Send packet
                Stream.Write(SystemBytesToBeSent, 0, _size)
                bytesSentThisSecond += _size
                SystemBytesToBeSentAvailable = False
            End If
        End If

    End Function

    Private Function HandleOutgoingUserData() As Boolean
        Dim tmp(1) As Byte
        Dim _size As UShort
        Dim notify As Boolean = False
        Static packet(0) As Byte

        If UserBytesToBeSentAvailable = True Then
            SyncLock UserBytesToBeSent
                Try
                    If (UserBytesToBeSent.Length - UserBytesToBeSent.Position) > blockSize Then
                        ' Send Channel
                        tmp(0) = UserOutputChannel
                        Stream.Write(tmp, 0, 1)

                        ' Send packet size
                        _size = blockSize
                        tmp = BitConverter.GetBytes(_size)
                        Stream.Write(tmp, 0, 2)

                        ' Send packet
                        If packet.Length <> _size Then ReDim packet(_size - 1)
                        UserBytesToBeSent.Read(packet, 0, _size)
                        'Client.NoDelay = True
                        Stream.Write(packet, 0, _size)
                        bytesSentThisSecond += 3 + _size

                        ' Check to see if we've sent it all...
                        If UserBytesToBeSent.Length = UserBytesToBeSent.Position Then
                            UserBytesToBeSentAvailable = False
                            notify = True
                        End If
                    Else
                        ' Send Channel
                        tmp(0) = UserOutputChannel
                        Stream.Write(tmp, 0, 1)

                        ' Send packet size
                        _size = Convert.ToUInt16(UserBytesToBeSent.Length - UserBytesToBeSent.Position)
                        tmp = BitConverter.GetBytes(_size)
                        Stream.Write(tmp, 0, 2)

                        ' Send packet
                        If packet.Length <> _size Then ReDim packet(_size - 1)
                        UserBytesToBeSent.Read(packet, 0, _size)
                        'Client.NoDelay = True
                        Stream.Write(packet, 0, _size)
                        bytesSentThisSecond += 3 + _size

                        UserBytesToBeSentAvailable = False
                        notify = True
                    End If
                Catch ex As Exception
                    ' Report error attempting to send user data.
                    Debug.WriteLine("Unexpected error in TcpCommClient\HandleOutgoingUserData: " & ex.Message)
                End Try
            End SyncLock

            ' Notify the user that the packet has been sent.
            If notify Then SystemMessage("UBS:" & UserOutputChannel)

            Return True
        Else
            Return False
        End If

    End Function

    'Private Function HandleOutgoingUserData() As Boolean
    '    Dim tmp(1) As Byte
    '    Dim _size As UShort

    '    If UserBytesToBeSentAvailable = True Then
    '        SyncLock UserBytesToBeSent.SyncRoot
    '            If UserBytesToBeSent.Length > blockSize Then
    '                ' Send Channel
    '                tmp(0) = UserOutputChannel
    '                Stream.Write(tmp, 0, 1)
    '                bytesSentThisSecond += 1

    '                ' Send packet size
    '                _size = blockSize
    '                tmp = BitConverter.GetBytes(_size)
    '                Stream.Write(tmp, 0, 2)
    '                bytesSentThisSecond += 2

    '                ' Send packet
    '                Stream.Write(GetSome(UserBytesToBeSent, blockSize, UserBytesToBeSentAvailable, True), 0, _size)
    '                bytesSentThisSecond += _size
    '            Else
    '                ' Send Channel
    '                tmp(0) = UserOutputChannel
    '                Stream.Write(tmp, 0, 1)
    '                bytesSentThisSecond += 1

    '                ' Send packet size
    '                _size = UserBytesToBeSent.Length
    '                tmp = BitConverter.GetBytes(_size)
    '                Stream.Write(tmp, 0, 2)
    '                bytesSentThisSecond += 2

    '                ' Send packet
    '                Stream.Write(UserBytesToBeSent, 0, _size)
    '                bytesSentThisSecond += _size
    '                UserBytesToBeSentAvailable = False
    '                SystemMessage("UBS")
    '            End If
    '        End SyncLock

    '        Return True
    '    Else

    '        Return False
    '    End If
    'End Function

    Private Function GetSome(ByRef bytes() As Byte, ByVal chunkToBreakOff As Integer,
                             ByRef bytesToBeSentAvailable As Boolean,
                             Optional ByVal theseAreUserBytes As Boolean = False) As Byte()

        Dim tmp(chunkToBreakOff - 1) As Byte
        Array.Copy(bytes, 0, tmp, 0, chunkToBreakOff)
        GetSome = tmp

        If bytes.Length = chunkToBreakOff Then
            bytesToBeSentAvailable = False
            If theseAreUserBytes Then SystemMessage("UBS")
        Else
            Dim tmp2(bytes.Length - chunkToBreakOff - 1) As Byte
            Array.Copy(bytes, chunkToBreakOff, tmp2, 0, bytes.Length - chunkToBreakOff)
            bytes = tmp2
        End If

    End Function

    Private Sub SystemMessage(ByVal MsgText As String)
        RcvBytes(StrToByteArray(MsgText), 255)
    End Sub

    ' Check to see if our app is closing (set in FormClosing event)
    Private Function theClientIsStopping() As Boolean

        If continue_running = False Then
            theClientIsStopping = True
        Else
            theClientIsStopping = False
        End If

    End Function

    Private Function CalculateMbps(Optional ByVal GetMbps As Boolean = False) As Decimal
        Static averagesCounter As Integer = 0
        Static tmr As Date = Now
        Static lastread As Int32 = 0
        Dim looper As Short = 0
        Dim tmp As Int32 = 0

        If mbpsOneSecondAverage Is Nothing Then ReDim mbpsOneSecondAverage(9)

        If Now >= tmr.AddMilliseconds(250) Then
            averagesCounter += 1
            If averagesCounter < 0 Then averagesCounter = 0
            Select Case averagesCounter
                Case 0
                    SyncLock (mbpsSyncObject)
                        Try
                            mbpsOneSecondAverage(averagesCounter) = bytesSentThisSecond + bytesReceivedThisSecond
                            bytesSentThisSecond = 0
                            bytesReceivedThisSecond = 0
                        Catch ex As Exception
                            averagesCounter = -1
                        End Try
                    End SyncLock
                Case 1
                    SyncLock (mbpsSyncObject)
                        Try
                            mbpsOneSecondAverage(averagesCounter) = bytesSentThisSecond + bytesReceivedThisSecond
                            bytesSentThisSecond = 0
                            bytesReceivedThisSecond = 0
                        Catch ex As Exception
                            averagesCounter = -1
                        End Try
                    End SyncLock
                Case 2
                    SyncLock (mbpsSyncObject)
                        Try
                            mbpsOneSecondAverage(averagesCounter) = bytesSentThisSecond + bytesReceivedThisSecond
                            bytesSentThisSecond = 0
                            bytesReceivedThisSecond = 0
                        Catch ex As Exception
                            averagesCounter = -1
                        End Try
                    End SyncLock
                Case 3
                    SyncLock (mbpsSyncObject)
                        Try
                            mbpsOneSecondAverage(averagesCounter) = bytesSentThisSecond + bytesReceivedThisSecond
                            bytesSentThisSecond = 0
                            bytesReceivedThisSecond = 0
                        Catch ex As Exception
                            averagesCounter = -1
                        End Try
                    End SyncLock
            End Select

            If averagesCounter > 2 Then averagesCounter = -1
            tmr = Now
        End If

        ' Did they ask us for the Mbps?
        If GetMbps Then
            For looper = 0 To 3
                SyncLock (mbpsSyncObject)
                    tmp += mbpsOneSecondAverage(looper)
                End SyncLock
            Next
            CalculateMbps = tmp
        Else
            CalculateMbps = 0
        End If

    End Function

    Private Sub Run()

        Dim puck(1) As Byte : puck(0) = 0
        Dim theBuffer(blockSize - 1) As Byte
        Dim tmp(1) As Byte
        Dim dataChannel As Integer = 0
        Dim packetSize As UShort = 0
        Dim bytesread As Integer
        Dim userOrSystemSwitcher As Integer = 0
        Dim PercentUsage As Short = -1
        Dim connectionLossTimer As Date


        Try

            Client = New TcpClient
            Client.Connect(IP, Port)

            ' Connection Accepted.
            Stream = Client.GetStream()

            ' Set the send and receive buffers to the maximum
            ' size allowable in this application...
            Client.Client.ReceiveBufferSize = 65535
            Client.Client.SendBufferSize = 65535

            ' no delay on partially filled packets...
            ' Send it all as fast as possible.
            Client.NoDelay = True

            ' Pass a message up to the user about our status.
            SystemMessage("Connected.")
            isRunning = True

            ' Start the communication loop
            Do
                ' Check to see if our app is shutting down.
                If theClientIsStopping() Then Exit Do

                ' Normal communications...
                If weHaveThePuck Then
                    ' Send user data if there is any to be sent.
                    userOrSystemSwitcher += 1
                    Select Case userOrSystemSwitcher
                        Case 1
                            HandleOutgoingUserData()
                        Case 2
                            HandleOutgoingInternalSystemMessage()
                    End Select
                    If userOrSystemSwitcher > 1 Then userOrSystemSwitcher = 0

                    ' After sending our data, send the puck
                    Stream.Write(puck, 0, 1)

                    ' Uncomment this to see control bit traffic as part of your Mbps 
                    'bytesSentThisSecond += 1
                    weHaveThePuck = False

                End If

                If theBuffer.Length < 2 Then ReDim theBuffer(1)

                ' Read in the control byte.
                Stream.Read(theBuffer, 0, 1)
                dataChannel = theBuffer(0)

                ' Uncomment this to see control bit traffic as part of your Mbps
                'bytesReceivedThisSecond += 1

                ' If it's just the puck (communictaion syncronization byte),
                ' set weHaveThePuck true and that's all. dataChannel 0 is 
                ' reserved for the puck.
                If dataChannel = 0 Then
                    weHaveThePuck = True
                Else
                    ' It's not the puck: It's an incoming packet.

                    ' Get the packet size:
                    tmp(0) = Convert.ToByte(Stream.ReadByte)
                    tmp(1) = Convert.ToByte(Stream.ReadByte)
                    packetSize = BitConverter.ToUInt16(tmp, 0)
                    If theBuffer.Length <> packetSize Then ReDim theBuffer(packetSize - 1)
                    bytesReceivedThisSecond += 2

                    ' Get the packet:
                    connectionLossTimer = Now
                    Do
                        ' Check to see if we're stopping...
                        If theClientIsStopping() Then Exit Do
                        ' Read bytes in...
                        bytesread += Stream.Read(theBuffer, bytesread, (packetSize - bytesread))

                        ' If it takes longer then 3 seconds to get a packet, we've lost connection.
                        If connectionLossTimer.AddSeconds(3) < Now Then Exit Try

                    Loop While bytesread < packetSize
                    bytesread = 0

                    ' Record bytes read for throttling...
                    bytesReceivedThisSecond += packetSize

                    ' Handle the packet...
                    If dataChannel > 250 Then
                        ' this is an internal system packet
                        HandleIncomingSystemMessages(theBuffer, dataChannel)
                    Else
                        ' Hand data off to the calling thread.
                        RcvBytes(theBuffer, dataChannel)
                    End If

                    If theClientIsStopping() Then Exit Do
                End If

                CalculateMbps(False)

                ' Measure and display the CPU usage of the client (this thread).


            Loop
        Catch ex As Exception
            ' An unexpected error.
            Debug.WriteLine("Unexpected error in Client\Run: " & ex.Message)
        End Try

        Try
            fileWriter.Close()
        Catch ex As Exception
        End Try

        Try

            Client.Client.Close()
            SystemMessage("Disconnected.")
        Catch ex As Exception
            ' An unexpected error.
            Debug.WriteLine("Unexpected error in Client\theClientIsStopping: " & ex.Message)
        End Try

        WrapUpIncomingFile()

        isRunning = False

    End Sub
End Class