locked
Multiple socket connections per thread ? RRS feed

  • Question

  • We are using VB.NET 2010.
    Our application receives client connection through socket, and currently each client has its own thread and socket.
    We would like to have multiple socket connections per thread, but we are having a problem doing it.
    Does any of you have example codes on how to do that ?
    Thank you.

    Below is our current program where each client has itsown tread and socket.

    Public class frmMyForm
    :
    Private Sub AcceptSocket(ByVal Sock As SocketTools.SocketWrench)
            Try
                Dim oSession As SessionClass
                Dim oThread As Threading.Thread

                oSession = New SessionClass
                oSession.Socket = Sock
                oThread = New Threading.Thread(AddressOf oSession.ThreadMain)
                oThread.SetApartmentState(ApartmentState.STA)
                oThread.Start()
            Catch ex As Exception
            End Try
        End Sub

    Public Class SessionClass
        Public MainForm As frmMyForm = Nothing
        Public Socket As SocketTools.SocketWrench = Nothing
        Private Messages As New Queue
        Private MessagesSync As Queue = Queue.Synchronized(Messages)
        Private Terminated As Boolean = False

        Public Sub ThreadMain()
            Dim strBuffer, sPacket As String
            Dim nResult As Integer

            Socket.AttachThread()
            Do While Not Terminated
                While MessagesSync.Count > 0 And Not (Terminated)
                    sPacket = MessagesSync.Dequeue
                    nResult = Socket.Write(sPacket)
                End While
                If Socket.IsReadable Then
                    strBuffer = ""
                    nResult = Socket.Read(strBuffer)
                    If nResult = 0 Then
                    ElseIf nResult = -1 Then
                    Else
                    End If
                End If
                If Socket.IsClosed Then
                    Exit Do
                End If
                If Not (Socket.IsConnected) Then
                    Exit Do
                End If
            Loop
            Socket.Disconnect()
            Socket = Nothing
            Messages = Nothing
            MessagesSync = Nothing
            MainForm.OnTerminated()
            Exit Sub
        End Sub

        Public Sub WriteLine(ByVal Line As String)
            MessagesSync.Enqueue(Line)
        End Sub
        Public Sub Terminate()
            Terminated = True
        End Sub 
    End Class

    Thursday, September 8, 2011 9:52 PM

Answers

  • Hmm... not sure what others have gotten confused about...

    You can run as many socket connections per thread as your code can process before timing out pending requests...

    The socket is opened on a single background thread.  It does not enforce exclusive access so other listeners can be started on the same socket.  Each thread has a single listener which is then accepting and queueing connections.  The thread worker is then iterating the connection queue, reading commands, processing them, and returning results.  This process continues for each connection in the queue.  When connections are closed, they are removed from the queue.  One can then start multiple instances of this background thread/listener/connection-processor.  In this way, one thread handles multiple connections, but multiple threads can be started when the current ones become too busy servicing their existing clients.

    Please consider this simple "Server" application:

    Public Class Form1
        Private WithEvents _StartServerButton As New Button
        Private _Manager As New ConnectionManager
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            _StartServerButton.Text = "Start"
            _StartServerButton.AutoSize = True
            Me.Controls.Add(_StartServerButton)
        End Sub
    
        Private Sub _StartServerButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _StartServerButton.Click
            If _StartServerButton.Text = "Start" Then
                _Manager.Start()
                _StartServerButton.Text = "Stop"
            Else
                _Manager.Stop()
                _StartServerButton.Text = "Start"
            End If
        End Sub
    End Class
    
    Public Class ConnectionManager
        Protected _Server As System.Net.Sockets.TcpListener
        Protected _Clients As New List(Of System.Net.Sockets.TcpClient)
        Protected _IsRunning As Boolean
    
        Public Sub [Stop]()
            _IsRunning = False
        End Sub
    
        Public Sub Start()
            _Server = New System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 23456)
            _Server.ExclusiveAddressUse = False
            _Server.Start()
            _Server.BeginAcceptTcpClient(AddressOf ClientConnect, Nothing)
            _IsRunning = True
            Dim task As New System.Threading.Tasks.Task(AddressOf DoWork)
            task.Start()
        End Sub
    
        Protected Sub ClientConnect(ByVal result As IAsyncResult)
            If _Server.Server.Connected Then
                Dim client As System.Net.Sockets.TcpClient = _Server.EndAcceptTcpClient(result)
                _Clients.Add(client)
                'Decide based on current load whether to accept more clients
                _Server.BeginAcceptTcpClient(AddressOf ClientConnect, Nothing)
            End If
        End Sub
    
        Protected Sub DoWork()
            While _IsRunning
                If _Clients.Count = 0 Then
                    System.Threading.Thread.Sleep(10)
                    Continue While
                End If
                For index As Integer = _Clients.Count - 1 To 0 Step -1
                    Dim client As System.Net.Sockets.TcpClient = _Clients(index)
                    If Not client.Connected Then
                        'when the client disconnects, drop the connection
                        _Clients.Remove(client)
                    Else
                        'Implement some appropriate logic for communication
                        If client.Available > 0 Then
                            Dim stream As System.Net.Sockets.NetworkStream = client.GetStream
                            'read request data
                            Dim data(client.Available - 1) As Byte
                            stream.Read(data, 0, data.Length)
                            'process
                            Array.Reverse(data)
                            'write response data
                            stream.Write(data, 0, data.Length)
                        End If
                    End If
                Next
            End While
            _Server.Stop()
            For Each client As System.Net.Sockets.TcpClient In _Clients
                If client.Connected Then client.Close()
            Next
            _Clients.Clear()
        End Sub
    End Class
    
    

    ...in conjunction with this simple "client" application:

    Public Class Form1
        Private WithEvents _ConnectButton As New Button
        Private WithEvents _DisconnectButton As New Button
        Private _ResultList As New ListBox
    
        Private client As New System.Net.Sockets.TcpClient
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            _ConnectButton.Text = "Connect"
            _ConnectButton.AutoSize = True
            _ConnectButton.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowAndShrink
            Controls.Add(_ConnectButton)
            _DisconnectButton.Text = "Disconnect"
            _DisconnectButton.AutoSize = True
            _DisconnectButton.Left = _ConnectButton.Left + _ConnectButton.Width + 38
            _DisconnectButton.Anchor = AnchorStyles.Top Or AnchorStyles.Right
            Controls.Add(_DisconnectButton)
            _ResultList.Dock = DockStyle.Bottom
            _ResultList.Height = ClientSize.Height - _ConnectButton.Height - 4
            Controls.Add(_ResultList)
        End Sub
    
        Private Sub _ConnectButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _ConnectButton.Click
            If Not client.Connected Then
                client.Connect(System.Net.IPAddress.Loopback, 23456)
            End If
            Dim stream As System.Net.Sockets.NetworkStream = client.GetStream
            If client.Available > 0 Then
                Dim result(client.Available - 1) As Byte
                stream.Read(result, 0, result.Length)
                _ResultList.Items.Add(Now.ToString)
                For Each b As Byte In result
                    _ResultList.Items.Add(b.ToString)
                Next
                _ConnectButton.Text = "Send Command"
            Else
                stream.Write(New Byte() {1, 2, 3}, 0, 3)
                _ConnectButton.Text = "Read Result"
            End If
        End Sub
    
        Private Sub _DisconnectButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _DisconnectButton.Click
            client.Close()
            client = New System.Net.Sockets.TcpClient
            _ConnectButton.Text = "Connect"
        End Sub
    End Class
    
    


    One can then start a single instance of the server application, and multiple instances of the client application, and have the various clients all communicate with the single server instance.

    In this example, the server will only use a single thread, but one could add logic to montior the performance of the "ConnectionManager" and then spawn new "ConnectionManager" objects as necessary. There would probably be a "CurrentManager" property on the server which gets updated to point to the ConnectionManager with the least amount of load, or a new ConnectionManager instance if all current instances are maxed.

    Please keep in mind that this is just a rough example and the actual logic needed will depend on exactly what the communication between the client and server needs to look like (the protocol being used), as well as the specific work which needs to be performed by the server.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    • Proposed as answer by Armin Zingler Friday, September 9, 2011 5:36 PM
    • Marked as answer by Mike Feng Sunday, September 18, 2011 7:05 AM
    Friday, September 9, 2011 5:14 PM

All replies

  • My educated guess is no. A thread can either do something or sleep. At any rate. a thread is monolithic. Before multithreading the answer was yes and the result would start a thread. I do not know if a thread can start another thread, but I sort of doubt it.

    Renee


    • Edited by Renee Culver Thursday, September 8, 2011 11:07 PM
    Thursday, September 8, 2011 11:06 PM
  • Hello Fniles.

    Have a look in this currently not so old message. 

    Don't look only to the marked answer but especially what Bill wrote about the update part.

    http://social.msdn.microsoft.com/Forums/en/adodotnetdataproviders/thread/dd21ca9d-4964-441e-b373-42debc6b9d1a


    Success
    Cor
    Friday, September 9, 2011 7:51 AM
  • Thank you for your reply.

    I am sorry if my question wasn't clear, but I am looking to create multiple TCP socket connection in a thread, not thread within thread, and not multiple database (sql server) connection per thread.
    Each client connect to the program using TCP Socket, currently each client has its own thread, and its thread has its own TCP socket. I would like to create multiple TCP socket per thread.

    Friday, September 9, 2011 2:20 PM
  • Usually there's a waithandle involved in network communication. Hence, the thread is blocked for other activity handling other requests. Therefore I think the answer is as Renee alread wrote: no.


    Armin
    Friday, September 9, 2011 3:47 PM
  • Thank you for the reply.

    Without having multiple socket connections per thread, since the application has many clients (like > 1000 clients, whic means it has 1000 threads), the application kept running out of memory.
    Do you have any suggestion on how to handle this situation ?

    Friday, September 9, 2011 3:53 PM
  • Yes...  Don't use explicit threading - use async sockets.  You will get greater performance and scalability - on windows (assuming xp or later) they are implemented using io completion ports. 
    Tom Shelton
    Friday, September 9, 2011 4:53 PM
  • My educated guess is no. A thread can either do something or sleep. At any rate. a thread is monolithic. Before multithreading the answer was yes and the result would start a thread. I do not know if a thread can start another thread, but I sort of doubt it.

    Renee



    In fact you can manage multiple connections on a single application thread - it used to be quite common in fact in unix environments.  Basically, you would maintain an array of sockets and then use the select function (a bsd socket function) to determine which, if any, connections were ready fro read/write operations.  This is still possible using the sockets built into .net, since all of the api's necessary are exposed by the socket class - but, it is probably more efficient to use the async programming model provided by the framework, since the async model is implemented using io completion ports...  I should test that out sometime :)

    And yes, you can spawn a thread from a thread. 


    Tom Shelton
    • Edited by Tom Shelton Friday, September 9, 2011 5:33 PM minor wording problem
    Friday, September 9, 2011 5:09 PM
  • Hmm... not sure what others have gotten confused about...

    You can run as many socket connections per thread as your code can process before timing out pending requests...

    The socket is opened on a single background thread.  It does not enforce exclusive access so other listeners can be started on the same socket.  Each thread has a single listener which is then accepting and queueing connections.  The thread worker is then iterating the connection queue, reading commands, processing them, and returning results.  This process continues for each connection in the queue.  When connections are closed, they are removed from the queue.  One can then start multiple instances of this background thread/listener/connection-processor.  In this way, one thread handles multiple connections, but multiple threads can be started when the current ones become too busy servicing their existing clients.

    Please consider this simple "Server" application:

    Public Class Form1
        Private WithEvents _StartServerButton As New Button
        Private _Manager As New ConnectionManager
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            _StartServerButton.Text = "Start"
            _StartServerButton.AutoSize = True
            Me.Controls.Add(_StartServerButton)
        End Sub
    
        Private Sub _StartServerButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _StartServerButton.Click
            If _StartServerButton.Text = "Start" Then
                _Manager.Start()
                _StartServerButton.Text = "Stop"
            Else
                _Manager.Stop()
                _StartServerButton.Text = "Start"
            End If
        End Sub
    End Class
    
    Public Class ConnectionManager
        Protected _Server As System.Net.Sockets.TcpListener
        Protected _Clients As New List(Of System.Net.Sockets.TcpClient)
        Protected _IsRunning As Boolean
    
        Public Sub [Stop]()
            _IsRunning = False
        End Sub
    
        Public Sub Start()
            _Server = New System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 23456)
            _Server.ExclusiveAddressUse = False
            _Server.Start()
            _Server.BeginAcceptTcpClient(AddressOf ClientConnect, Nothing)
            _IsRunning = True
            Dim task As New System.Threading.Tasks.Task(AddressOf DoWork)
            task.Start()
        End Sub
    
        Protected Sub ClientConnect(ByVal result As IAsyncResult)
            If _Server.Server.Connected Then
                Dim client As System.Net.Sockets.TcpClient = _Server.EndAcceptTcpClient(result)
                _Clients.Add(client)
                'Decide based on current load whether to accept more clients
                _Server.BeginAcceptTcpClient(AddressOf ClientConnect, Nothing)
            End If
        End Sub
    
        Protected Sub DoWork()
            While _IsRunning
                If _Clients.Count = 0 Then
                    System.Threading.Thread.Sleep(10)
                    Continue While
                End If
                For index As Integer = _Clients.Count - 1 To 0 Step -1
                    Dim client As System.Net.Sockets.TcpClient = _Clients(index)
                    If Not client.Connected Then
                        'when the client disconnects, drop the connection
                        _Clients.Remove(client)
                    Else
                        'Implement some appropriate logic for communication
                        If client.Available > 0 Then
                            Dim stream As System.Net.Sockets.NetworkStream = client.GetStream
                            'read request data
                            Dim data(client.Available - 1) As Byte
                            stream.Read(data, 0, data.Length)
                            'process
                            Array.Reverse(data)
                            'write response data
                            stream.Write(data, 0, data.Length)
                        End If
                    End If
                Next
            End While
            _Server.Stop()
            For Each client As System.Net.Sockets.TcpClient In _Clients
                If client.Connected Then client.Close()
            Next
            _Clients.Clear()
        End Sub
    End Class
    
    

    ...in conjunction with this simple "client" application:

    Public Class Form1
        Private WithEvents _ConnectButton As New Button
        Private WithEvents _DisconnectButton As New Button
        Private _ResultList As New ListBox
    
        Private client As New System.Net.Sockets.TcpClient
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            _ConnectButton.Text = "Connect"
            _ConnectButton.AutoSize = True
            _ConnectButton.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowAndShrink
            Controls.Add(_ConnectButton)
            _DisconnectButton.Text = "Disconnect"
            _DisconnectButton.AutoSize = True
            _DisconnectButton.Left = _ConnectButton.Left + _ConnectButton.Width + 38
            _DisconnectButton.Anchor = AnchorStyles.Top Or AnchorStyles.Right
            Controls.Add(_DisconnectButton)
            _ResultList.Dock = DockStyle.Bottom
            _ResultList.Height = ClientSize.Height - _ConnectButton.Height - 4
            Controls.Add(_ResultList)
        End Sub
    
        Private Sub _ConnectButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _ConnectButton.Click
            If Not client.Connected Then
                client.Connect(System.Net.IPAddress.Loopback, 23456)
            End If
            Dim stream As System.Net.Sockets.NetworkStream = client.GetStream
            If client.Available > 0 Then
                Dim result(client.Available - 1) As Byte
                stream.Read(result, 0, result.Length)
                _ResultList.Items.Add(Now.ToString)
                For Each b As Byte In result
                    _ResultList.Items.Add(b.ToString)
                Next
                _ConnectButton.Text = "Send Command"
            Else
                stream.Write(New Byte() {1, 2, 3}, 0, 3)
                _ConnectButton.Text = "Read Result"
            End If
        End Sub
    
        Private Sub _DisconnectButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _DisconnectButton.Click
            client.Close()
            client = New System.Net.Sockets.TcpClient
            _ConnectButton.Text = "Connect"
        End Sub
    End Class
    
    


    One can then start a single instance of the server application, and multiple instances of the client application, and have the various clients all communicate with the single server instance.

    In this example, the server will only use a single thread, but one could add logic to montior the performance of the "ConnectionManager" and then spawn new "ConnectionManager" objects as necessary. There would probably be a "CurrentManager" property on the server which gets updated to point to the ConnectionManager with the least amount of load, or a new ConnectionManager instance if all current instances are maxed.

    Please keep in mind that this is just a rough example and the actual logic needed will depend on exactly what the communication between the client and server needs to look like (the protocol being used), as well as the specific work which needs to be performed by the server.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    • Proposed as answer by Armin Zingler Friday, September 9, 2011 5:36 PM
    • Marked as answer by Mike Feng Sunday, September 18, 2011 7:05 AM
    Friday, September 9, 2011 5:14 PM
  • Apart from the fact that this keeps the thread and CPU (core) always 100% busy even if there is no data available, I proposed it as the answer.
    Armin
    Friday, September 9, 2011 5:36 PM
  • Reed...  Why not just use the async socket methods throughout?  It simply is going to be much more scalable than trying to manage this your self...


    Tom Shelton
    Friday, September 9, 2011 5:43 PM
  • Armin....

    It is not the answer - why is everyone so afraid of doing this the right way.  Use the async programing model. 


    Tom Shelton
    Friday, September 9, 2011 7:00 PM
  • It is what the OP was asking, not what I or you consider the best solution.
    Armin
    Friday, September 9, 2011 7:16 PM
  • This was a hasty example sorry... I saw the "you can't do it" replies and got in a rush.

    On further thought, I guess you only need one Listener on the server and let it decide which ConnectionManager to pass the connection off to.  So the listener could be abstracted from the ConnectionManager class.

    In any event, the point was that the work to be done is what will tie up a particular thread, but that thread may be able to handle the work of several clients before becomming too busy to service new clients.  So all you have to do is assign a particular number of client connections to each worker thread, and make new threads when the existing ones get too busy.

    @Armin: I was only concerned with waiting when there were no connections - the post already calls for modifing the request/response logic based on the protocol and purpose.  But it is a good thing to point out.

    @Tom: Agreed; mostly LOL  I did not use async reads and writes so as to keep the example code easy to follow.  The question was specifically about how to manage multiple client connections per thread so I was really just after the quickest possible example of one thread managing the request/processing/response cycle for a collection of assigned client connections. At some point we have to split the processing off into multiple threads otherwise client request 6000 may time out before the previous 5999 finish processing their last command sets.

    By all means though - anyone else is welcome to add a better example to the thread!


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Friday, September 9, 2011 7:17 PM
  • First of all, I agree with tom.

    Secondly, what problem is the OP trying to solve? The op has told us nothing about the system. I agree that the system does seem to have problems.

    Renee

    Friday, September 9, 2011 8:09 PM
  • Armin...

    Sometimes, what the OP asks for isn't always the right answer :) 

     

     


    Tom Shelton
    Friday, September 9, 2011 8:32 PM
  • Reed - I will try and whip a async example.  Just not much time today :)  I'm going to have to start archiving this stuff...

     

     


    Tom Shelton
    Friday, September 9, 2011 8:36 PM
  • Tom,  (Hello BTW!  )

    Yes, but on other occassions, I'm told not be the one who decides this. :)


    Armin
    Friday, September 9, 2011 8:53 PM
  • Doesn't this also create one thread per socket internally because of the Waithandles? I don't know.

    Armin
    Friday, September 9, 2011 8:54 PM
  • Once again, That's my point. We aren't doing our jobs if we merely answer an unknown OP's questions.

    Once again I agree with tom. Often the novice by definition does not have any idea of an approach much less a design. Yet, nonr-the-less, I see people not asking questions of people who are really asking design questions.

    Renee


    Friday, September 9, 2011 9:18 PM
  • Tom,  (Hello BTW!  )

    Yes, but on other occassions, I'm told not be the one who decides this. :)


    Armin


    First - Hello - back at you!  I was  bit surprised to see you on here...  Of course, I haven't been around here much lately :)

    Second - I hear ya.   It can really be hard to differentiate.  Ultimately, it's the OP that is going to decide what the correct answer is :)


    Tom Shelton
    Friday, September 9, 2011 9:40 PM
  •  

    Let us please try to keep this thead on topic.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, September 10, 2011 4:59 AM
  • Thank you Reed for the sample codes. It is what I was looking for.
    Thenk you everybody for the input.
    Monday, September 12, 2011 3:55 PM