TCPClient adds every 25 CPU usage per client

Twiinz

Member
Joined
Jul 18, 2013
Messages
6
Programming Experience
5-10
Hello, i was learning about sending messages to the server -> client -> server, however after every client that gets connected it adds about 25% CPU. So after 4 clients the process is using 100% of CPU usage.

Code:
Imports System.Net ' for IPAddress
Imports System.Net.Sockets 'for TcpListener
Imports System.Text

Module Module1
    Dim servePort As Integer = 28960
    Dim requestCount As Integer
    Dim bytesFrom(10024) As Byte
    Dim dataFromClient As String
    Dim sendBytes As [Byte]()
    Dim serverResponse As String
    Dim rCount As String
    Sub Main()
        Dim serverSocket As New TcpListener(servePort)
        Dim clientSocket As TcpClient
        Dim counter As Integer
        Console.Title = ("Server")
        msg("Starting Server...")
        serverSocket.Start()
        msg("Server started on port " & servePort)
        msg("")
        counter = 0




        While (True)
            counter += 1
            clientSocket = serverSocket.AcceptTcpClient()
            If clientSocket.Client.LocalEndPoint.ToString = "127.0.0.1:28961" Then
                clientSocket.Close()
                Exit While
            End If

            msg("Client " + clientSocket.Client.LocalEndPoint.ToString + " connected.")
            Dim client As New handleClinet
            Dim networkStream As NetworkStream = _
                            clientSocket.GetStream()
            client.startClient(clientSocket, Convert.ToString(counter))

        End While

        clientSocket.Close()
        serverSocket.Stop()

        Console.ReadLine()
    End Sub

    Sub msg(ByVal mesg As String)
        mesg.Trim()
        Console.WriteLine(mesg)
    End Sub

    Public Class handleClinet
        Dim clientSocket As TcpClient
        Dim clNo As String
        Public Sub startClient(ByVal inClientSocket As TcpClient, _
        ByVal clineNo As String)
            Me.clientSocket = inClientSocket
            Me.clNo = clineNo
            Dim ctThread As Threading.Thread = New Threading.Thread(AddressOf doChat)
            ctThread.Start()
        End Sub
        Private Sub doChat()
            requestCount = 0

            While (True)
                Try
                    requestCount = requestCount + 1
                    Dim ip As String = clientSocket.Client.LocalEndPoint.ToString
                    Dim networkStream As NetworkStream = _
                            clientSocket.GetStream()
                    networkStream.Read(bytesFrom, 0, CInt(clientSocket.ReceiveBufferSize))
                    dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom)
                    dataFromClient = _
                    dataFromClient.Substring(0, dataFromClient.IndexOf("$"))
                    msg("<< " & dataFromClient)
                    rCount = Convert.ToString(requestCount)

                    If dataFromClient.ToLower = "hydra_ver" Then
                        serverResponse = "0.94"
                    Else
                       serverResponse = "Unknown Command"
                    End If


                    Else
                        serverResponse = "Unknown Command"
                    End If
                    sendBytes = Encoding.ASCII.GetBytes(serverResponse)
                    networkStream.Write(sendBytes, 0, sendBytes.Length)
                    networkStream.Flush()
                    msg(">> " & serverResponse)
                Catch ex As Exception

                End Try

            End While

        End Sub
    End Class
End Module
I didn't figure out what the problem could be?
 

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
There are a couple of issues with the code above... TCP connections are kind of complicated to wrap your head around if you are a beginner, so I rewrote your example with lots of comments to show you how to do it. The most important difference between our code is that your server can only handle one connection at a time. If someone tries to connect while you are already servicing one connection, they will get no answer. Mine services the request on a separate thread, and goes back to listening immediately, so there can be multiple clients connected to a single server. There were also a lot of structural problems with your code, too much procedural and not enough OOP. Modules aren't terribly useful in modern VB, in doubt always choose a class over it. Let me know if you have any questions...

Imports System.Net.Sockets
Imports System.Net
Imports System.Threading
Imports System.Text

Public Class MyServer
    ' Default server port is 28960
    Private Const DefaultServerPort As Integer = 28960
    ' Server version string?
    Private Const HydraVer As String = "0.94"

    ' Necessary for the asynchronous connection handling
    Private ReadOnly _connectionWaitHandle As New AutoResetEvent(False)

    ' Private fields for request counts.
    Private _requestsCount As Integer = 0
    Private _requestsHandledCount As Integer = 0

    ' Read only properties to expose the request counts.
    Public ReadOnly Property RequestsCount As Integer
        Get
            Return _requestsCount
        End Get
    End Property
    Public ReadOnly Property RequestsHandledCount As Integer
        Get
            Return _requestsHandledCount
        End Get
    End Property

    ' Get/Set the TCP port to listen on.
    Public Property ServerPort As Integer = DefaultServerPort

    ' Default constructor, immediately starts listening on default port.
    Public Sub New()
        StartServer()
    End Sub

    ' Constructor overload, immediately starts listening on specified port.
    Public Sub New(port As Integer)
        ServerPort = port
        StartServer()
    End Sub

    ' Start the server and starts listening for connections.
    Private Sub StartServer()
        Dim bStarted As Boolean = True

        Dim serverListener As New TcpListener(IPAddress.Any, ServerPort)

        Try
            ' Try to start the server...
            serverListener.Start()
        Catch ex As SocketException
            ' If something goes wrong report the error.
            ReportSocketException(ex)
            bStarted = False
        Finally
            ' Otherwise...
            If bStarted = True Then
                Console.WriteLine("Server started on port " & ServerPort)

                ' Main dispatcher loop
                Do
                    ' Wait for connection, and handle it asynchronously, so we can have more than one simultaneous clients.
                    Console.WriteLine("Waiting for connection...")
                    serverListener.BeginAcceptTcpClient(AddressOf HandleAsyncConnection, serverListener)

                    ' Increment the requests counter when a request comes in.
                    _requestsCount += 1

                    ' Wait for the connection to start being handled before starting to listen again.
                    _connectionWaitHandle.WaitOne()
                    _connectionWaitHandle.Reset()
                Loop

            End If
        End Try

    End Sub

    ' We have received a connection request, handle it.
    Private Sub HandleAsyncConnection(asyncResult As IAsyncResult)
        ' Recover the reference to the listener that we passed earlier and get a tcpclient reference from it.
        Dim serverListener As TcpListener = asyncResult.AsyncState
        Dim clientSocket As TcpClient = serverListener.EndAcceptTcpClient(asyncResult)

        ' Signal the main thread that we have started handling this request.
        _connectionWaitHandle.Set()

        ' Filter the incoming connection by endpoint, refuse any connection from "127.0.0.1:28961"
        Dim localEndPoint As String = clientSocket.Client.LocalEndPoint.ToString
        If localEndPoint = "127.0.0.1:28961" Then
            clientSocket.Close()
        Else
            ' Otherwise report the connection and service the request.
            Console.WriteLine("Client " & localEndPoint & " connected.")
            ServiceConnection(clientSocket)
            ' Increment the request handled counter when a request is handled.
            _requestsHandledCount += 1
        End If

        clientSocket.Close()
    End Sub

    ' Service any incoming request after a connection has been successfully established.
    Private Sub ServiceConnection(clientSocket As TcpClient)
        ' Declare some local variables...
        Dim InputBuffer() As Byte = Nothing
        Dim OutputBuffer() As Byte = Nothing

        Dim NetStream As NetworkStream = Nothing

        Dim strRequest, strResponse As String

        Try
            ' Get the network stream reference from the passed TcpClient and read the received data.
            NetStream = clientSocket.GetStream()
            NetStream.Read(InputBuffer, 0, clientSocket.ReceiveBufferSize)

            ' Format the data and extract the request string.
            strRequest = Encoding.ASCII.GetString(InputBuffer).Split("$")(0).ToLower

            ' Report the request received.
            Console.WriteLine("<< " & strRequest)
        Catch ex As SocketException
            ' If something goes wrong report the error and discard the request.
            ReportSocketException(ex)
            Exit Sub
        End Try

        ' Analyze the request and select a response.
        Select Case strRequest
            Case "hydra_ver"
                strResponse = HydraVer
            Case Else
                strResponse = "Unknown Command"
        End Select

        Try
            ' Send the response back to the client.
            OutputBuffer = Encoding.ASCII.GetBytes(strResponse)
            NetStream.Write(OutputBuffer, 0, OutputBuffer.Length)

            ' Report the response sent.
            Console.WriteLine(">> " & strResponse)
        Catch ex As SocketException
            ' If something goes wrong report the error
            ReportSocketException(ex)
        End Try
    End Sub

    ' Report SocketExceptions
    Private Sub ReportSocketException(ex As SocketException)
        Console.WriteLine("SocketException (" & ex.ErrorCode & ") " & ex.Message)
    End Sub

End Class
 

Twiinz

Member
Joined
Jul 18, 2013
Messages
6
Programming Experience
5-10
Wow, thanks for your help!

Yes, i'm just starting with the whole networking in VB. So i really appreciate that you have added these commands in your script, i can really use them.
 

Twiinz

Member
Joined
Jul 18, 2013
Messages
6
Programming Experience
5-10
When I execute the code as Console, the console closes instantly. I don't think that is correct.
 

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
Press alt-ctrl-E in VS, and check everything in the Thrown column. you should see an exception when it crashes.
 

Twiinz

Member
Joined
Jul 18, 2013
Messages
6
Programming Experience
5-10
Well the first thing in the error log it says:

Error 1 'Sub Main' was not found in 'server'. server, the only startup object is: Sub Main
 

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
What I posted above is a class, in your Main() sub. you need to instanciate it to use it.

Private objServer As New MyServer(intPortNumber)


And then wait in a loop for some user interaction to close the application.
 

Twiinz

Member
Joined
Jul 18, 2013
Messages
6
Programming Experience
5-10
I have tried that, and the server is listening now :).

However when i try to connect with the client to the server the server crashes and gives the following error

Code:
Value cannot be null.
Parameter name: buffer
Line: NetStream.Read(InputBuffer, 0, clientSocket.ReceiveBufferSize)

Here is the client code

Code:
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Code:
Dim clientSocket As New System.Net.Sockets.TcpClient()
Code:
clientSocket.Connect("127.0.0.1", 28960)
Code:
Function SendCommand(ByVal cmd As String)
        Dim serverStream As NetworkStream = clientSocket.GetStream()
        Dim buffSize As Integer
        Dim outStream As Byte() = _
        System.Text.Encoding.ASCII.GetBytes(cmd & "$")
        serverStream.Write(outStream, 0, outStream.Length)
        serverStream.Flush()

        Dim inStream(10024) As Byte
        buffSize = clientSocket.ReceiveBufferSize
        serverStream.Read(inStream, 0, buffSize)
        Dim returndata As String = _
        System.Text.Encoding.ASCII.GetString(inStream)

        Return returndata
    End Function
The weird thing about the server is that the NetStream.Read(InputBuffer, 0, clientSocket.ReceiveBufferSize) code is in TRY tags, yet the server stops.
 
Last edited:

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
You have to explicitly initialize the buffer array to the right length.

Dim inStream(clientSocket.ReceiveBufferSize) As Byte
Dim outStream(clientSocket.SendBufferSize) As Byte


Also, you have to strip NULL chars from your response after GetString.

returndata = returndata.Replace(vbNullChar, "")

This should get you rolling...
 

Twiinz

Member
Joined
Jul 18, 2013
Messages
6
Programming Experience
5-10
Jep, got it working as we speak :).

The only problem now is when I send out 2 commando's to the server, the second command doesn't work. Probably it disconnects after the first command.


The error
Code:
Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.
 

Chewbaca

New member
Joined
Jun 18, 2015
Messages
1
Programming Experience
Beginner
Sorry to be the guy to necro a really old thread but I've been fighting with this for the last couple of days. I have a few hundred users on my app and my current TCPclient slowly wrecks my CPU over time causing a whole mess of problems.

Would really appreciate if someone could point out what I'm doing wrong with this because I've followed the thread and my server is still crashing.

The server:
Code:
Module Module1

    Sub Main()


        Dim objServer As New MyServer(881)
    End Sub


End Module
Here is the only difference I could see. The original thread used:

Code:
Private[COLOR=#3E3E3E][FONT=Consolas] [/FONT][/COLOR]objServer As[COLOR=#3E3E3E][FONT=Consolas] [/FONT][/COLOR]New[COLOR=#3E3E3E][FONT=Consolas] [/FONT][/COLOR]MyServer(intPortNumber)
But that wasn't working I was forced to set a Dim instead (and I set a port as well).

Client is pretty basic including the changes which were causing a crash before:

Code:
Imports System.NetImports System.Net.Sockets
Imports System.Text


Public Class Form1
    Dim clientSocket As New System.Net.Sockets.TcpClient()
  


    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        clientSocket.Connect("127.0.0.1", 881)
    End Sub


    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click


        Dim Version As String = "version"
        SendCommand("Version")
    End Sub


    Function SendCommand(ByVal cmd As String)
        Dim serverStream As NetworkStream = clientSocket.GetStream()
        Dim buffSize As Integer
        Dim outStream(clientSocket.SendBufferSize) As Byte
        System.Text.Encoding.ASCII.GetBytes(cmd & "$")


        serverStream.Write(outStream, 0, outStream.Length)
        serverStream.Flush()


        Dim inStream(clientSocket.ReceiveBufferSize) As Byte
        buffSize = clientSocket.ReceiveBufferSize
        serverStream.Read(inStream, 0, buffSize)
        Dim returndata As String = _
        System.Text.Encoding.ASCII.GetString(inStream)
        returndata = returndata.Replace(vbNullChar, "")


        Return returndata
    End Function


End Class
Server loads a console and listens on port 881. But the second I start the client it doesn't even get far enough To sending anything immediately crashes the server on connection.

Console dump before the crash:

Code:
Server started on port 881
Waiting for connection...
Client 127.0.0.1:881 connected.
Waiting for connection...

Unhandled exception yada yada yada Value cannot be null parameter name buffer
Exact problem OP was having but apparently fixed after setting the buffer array to the right length. Any help on this would be really appreciated. >_<
 
Top Bottom