TCP Server Threadpool

  • VB.NET

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    TCP Server Threadpool

    Hallo liebe Community,

    ich habe vor einigen Wochen im Internet folgenden Code gefunden, ganz folgen kann ich allerdings nicht.

    Der server reagiert auf eine anfrage vom clienten mit einer antwort... soweit sogut, nun frage ich mich aber wie bzw wo
    kann ich eine bestimmte Verbindung ansprechen die im Pool ist.

    beispielsweise eine private nachricht von client1 zu client2... ist dies mit dem server machbar ?(


    desweiteren ist mir aufgefallen das der Server Exceptions wirft sobald ein Client verbunden ist was wohl mit dem "TimeOut" der verbindung zu tun hat... dennoch arbeitet der server weiter und unterbricht keine verbindugnen ( ist ja auch nicht eingestellt wie man sehen kann) dennoch frage ich mich ob das so richtig ist ?(


    schaut euch den code einfachmal an... :thumbsup:

    ich freue mich über jede hilfe die mir gegeben werden kann :)

    LG
    Marvin


    Server
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.IO
    3. Imports System.Net.Sockets
    4. Imports System.Text
    5. Imports System.Collections
    6. Imports System.Threading
    7. Friend Class ClientConnectionPool
    8. ' Creates a synchronized wrapper around the Queue.
    9. Private SyncdQ As Queue = Queue.Synchronized(New Queue())
    10. Public Sub Enqueue(ByVal client As ClientHandler)
    11. SyncdQ.Enqueue(client)
    12. End Sub
    13. Public Function Dequeue() As ClientHandler
    14. Return DirectCast(SyncdQ.Dequeue(), ClientHandler)
    15. End Function
    16. Public ReadOnly Property Count() As Integer
    17. Get
    18. Return SyncdQ.Count
    19. End Get
    20. End Property
    21. Public ReadOnly Property SyncRoot() As Object
    22. Get
    23. Return SyncdQ.SyncRoot
    24. End Get
    25. End Property
    26. End Class ' class ClientConnectionPool
    27. Friend Class ClientService
    28. Private Const NUM_OF_THREAD As Integer = 10
    29. Private ConnectionPool As ClientConnectionPool
    30. Private ContinueProcess As Boolean = False
    31. Private ThreadTask(NUM_OF_THREAD - 1) As Thread
    32. Public Sub New(ByVal ConnectionPool As ClientConnectionPool)
    33. Me.ConnectionPool = ConnectionPool
    34. End Sub
    35. Public Sub Start()
    36. ContinueProcess = True
    37. ' Start threads to handle Client Task
    38. For i As Integer = 0 To ThreadTask.Length - 1
    39. ThreadTask(i) = New Thread(New ThreadStart(AddressOf Me.Process))
    40. ThreadTask(i).Start()
    41. Next i
    42. End Sub
    43. Private Sub Process()
    44. Do While ContinueProcess
    45. Dim client As ClientHandler = Nothing
    46. SyncLock ConnectionPool.SyncRoot
    47. If ConnectionPool.Count > 0 Then
    48. client = ConnectionPool.Dequeue()
    49. End If
    50. End SyncLock
    51. If client IsNot Nothing Then
    52. client.Process() ' Provoke client
    53. ' if client still connect, schedufor later processingle it
    54. If client.Alive Then
    55. ConnectionPool.Enqueue(client)
    56. End If
    57. End If
    58. Thread.Sleep(100)
    59. Loop
    60. End Sub
    61. Public Sub [Stop]()
    62. ContinueProcess = False
    63. For i As Integer = 0 To ThreadTask.Length - 1
    64. If ThreadTask(i) IsNot Nothing AndAlso ThreadTask(i).IsAlive Then
    65. ThreadTask(i).Join()
    66. End If
    67. Next i
    68. ' Close all client connections
    69. Do While ConnectionPool.Count > 0
    70. Dim client As ClientHandler = ConnectionPool.Dequeue()
    71. client.Close()
    72. Console.WriteLine("Client connection is closed!")
    73. Loop
    74. End Sub
    75. End Class ' class ClientService
    76. Public Class SynchronousSocketListener
    77. Private Const portNum As Integer = 10116
    78. Public Shared Sub StartListening()
    79. Dim ClientTask As ClientService
    80. ' Client Connections Pool
    81. Dim ConnectionPool As New ClientConnectionPool()
    82. ' Client Task to handle client requests
    83. ClientTask = New ClientService(ConnectionPool)
    84. ClientTask.Start()
    85. Dim listener As New TcpListener(portNum)
    86. Try
    87. listener.Start()
    88. Dim TestingCycle As Integer = 3 ' Number of testing cycles
    89. Dim ClientNbr As Integer = 0
    90. ' Start listening for connections.
    91. Console.WriteLine("Waiting for a connection...")
    92. Do While TestingCycle > 0
    93. Dim handler As TcpClient = listener.AcceptTcpClient()
    94. If handler IsNot Nothing Then
    95. ClientNbr += 1
    96. Console.WriteLine("Client#{0} accepted!", ClientNbr)
    97. ' An incoming connection needs to be processed.
    98. ConnectionPool.Enqueue(New ClientHandler(handler))
    99. TestingCycle -= 1
    100. Else
    101. Exit Do
    102. End If
    103. Loop
    104. listener.Stop()
    105. ' Stop client requests handling
    106. ClientTask.Stop()
    107. Catch e As Exception
    108. Console.WriteLine(e.ToString())
    109. End Try
    110. Console.WriteLine(ControlChars.Lf & "Hit enter to continue...")
    111. Console.Read()
    112. End Sub
    113. Public Shared Function Main(ByVal args() As String) As Integer
    114. StartListening()
    115. Return 0
    116. End Function
    117. End Class
    118. Friend Class ClientHandler
    119. Private ClientSocket As TcpClient
    120. Private networkStream As NetworkStream
    121. Private ContinueProcess As Boolean = False
    122. Private bytes() As Byte ' Data buffer for incoming data.
    123. Private sb As New StringBuilder() ' Received data string.
    124. Private data As String = Nothing ' Incoming data from the client.
    125. Public Sub New(ByVal ClientSocket As TcpClient)
    126. ClientSocket.ReceiveTimeout = 100 ' 100 miliseconds
    127. Me.ClientSocket = ClientSocket
    128. networkStream = ClientSocket.GetStream()
    129. bytes = New Byte(ClientSocket.ReceiveBufferSize - 1){}
    130. ContinueProcess = True
    131. End Sub
    132. Public Sub Process()
    133. Try
    134. Dim BytesRead As Integer = networkStream.Read(bytes, 0, CInt(bytes.Length))
    135. If BytesRead > 0 Then
    136. ' There might be more data, so store the data received so far.
    137. sb.Append(Encoding.ASCII.GetString(bytes, 0, BytesRead))
    138. Else
    139. ' All the data has arrived; put it in response.
    140. ProcessDataReceived()
    141. End If
    142. Catch e1 As IOException
    143. ' All the data has arrived; put it in response.
    144. ProcessDataReceived()
    145. Catch e2 As SocketException
    146. networkStream.Close()
    147. ClientSocket.Close()
    148. ContinueProcess = False
    149. Console.WriteLine("Conection is broken!")
    150. End Try
    151. End Sub ' Process()
    152. Private Sub ProcessDataReceived()
    153. If sb.Length > 0 Then
    154. Dim bQuit As Boolean = (String.Compare(sb.ToString(), "quit", True) = 0)
    155. data = sb.ToString()
    156. sb.Length = 0 ' Clear buffer
    157. Console.WriteLine("Text received from client:")
    158. Console.WriteLine(data)
    159. Dim response As New StringBuilder()
    160. response.Append("Received at ")
    161. response.Append(Date.Now.ToString())
    162. response.Append(ControlChars.CrLf)
    163. response.Append(data)
    164. ' Echo the data back to the client.
    165. Dim sendBytes() As Byte = Encoding.ASCII.GetBytes(response.ToString())
    166. networkStream.Write(sendBytes, 0, sendBytes.Length)
    167. ' Client stop processing
    168. If bQuit Then
    169. networkStream.Close()
    170. ClientSocket.Close()
    171. ContinueProcess = False
    172. End If
    173. End If
    174. End Sub
    175. Public Sub Close()
    176. networkStream.Close()
    177. ClientSocket.Close()
    178. End Sub
    179. Public ReadOnly Property Alive() As Boolean
    180. Get
    181. Return ContinueProcess
    182. End Get
    183. End Property
    184. End Class ' class ClientHandler


    Client
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Net.Sockets
    3. Imports System.Text
    4. Friend Class TcpClientTest
    5. Private Const portNum As Integer = 10116
    6. Public Shared Sub Main()
    7. Dim tcpClient As New TcpClient()
    8. Try
    9. tcpClient.Connect("localhost", portNum)
    10. Dim networkStream As NetworkStream = tcpClient.GetStream()
    11. If networkStream.CanWrite AndAlso networkStream.CanRead Then
    12. Dim DataToSend As String = ""
    13. Do While DataToSend <> "quit"
    14. Console.WriteLine(ControlChars.Lf & "Type a text to be sent:")
    15. DataToSend = Console.ReadLine()
    16. If DataToSend.Length = 0 Then
    17. Exit Do
    18. End If
    19. Dim sendBytes() As Byte = Encoding.ASCII.GetBytes(DataToSend)
    20. networkStream.Write(sendBytes, 0, sendBytes.Length)
    21. ' Reads the NetworkStream into a byte buffer.
    22. Dim bytes(tcpClient.ReceiveBufferSize - 1) As Byte
    23. Dim BytesRead As Integer = networkStream.Read(bytes, 0, CInt(tcpClient.ReceiveBufferSize))
    24. ' Returns the data received from the host to the console.
    25. Dim returndata As String = Encoding.ASCII.GetString(bytes, 0, BytesRead)
    26. Console.WriteLine("This is what the host returned to you: " & ControlChars.CrLf & "{0}", returndata)
    27. Loop
    28. networkStream.Close()
    29. tcpClient.Close()
    30. ElseIf Not networkStream.CanRead Then
    31. Console.WriteLine("You can not write data to this stream")
    32. tcpClient.Close()
    33. ElseIf Not networkStream.CanWrite Then
    34. Console.WriteLine("You can not read data from this stream")
    35. tcpClient.Close()
    36. End If
    37. Catch e1 As SocketException
    38. Console.WriteLine("Sever not available!")
    39. Catch e2 As System.IO.IOException
    40. Console.WriteLine("Sever not available!")
    41. Catch e As Exception
    42. Console.WriteLine(e.ToString())
    43. End Try
    44. End Sub ' Main()
    45. End Class ' class TcpClientTest {
    Imperiums - Fortschritt in ein neues Zeitalter (Remake 2021)
    jo, code im INet finden kann man viele.
    Kannst auch mal im vbp-tutorial-Bereich, Sparte "Internet/Lan" gucken, da gibts noch mehr so TCP-codes.

    Du kannst übrigens nicht erwarten, dass irgendeiner dein hier eingestelltes snippet ( > 200 zeilen) duchguckt, und dir dann sagen kann, ob und wie du damit eine peer to peer - Verbindung aufbauen kannst.
    (Ich für mein Teil denke: "keine Chance, optionales peer to peer bedarf eines durchdachten bidirektionalen Befehls-Protokolls - und davon sind in den 200 Zeilen sicherlich nichtmal Ansätze enthalten.")

    Mein TCP-Tut "TcpKommunikation" hat sowas drinne, aber ist sehr anspruchsvoller code.
    Und nur mit finden, aber nicht verstehen, kommt man bei so anspruchsvollen Sachen in keinem Fall zum Ziel.
    Ja im Netz sind sehr viele Codes
    und auch dein Code habe ich mir mal angesehen
    mal mit mehr und weniger Fragezeichen

    wie ich immer sage ich bin ja auch immer wieder gerne bereit etwas dazu zu lernen und vor allem wenn es um dieses Thema geht :)


    ich ich versuche halt einen guten und stabilen Server zu basteln, der sowohl viele clienten
    verwalten kann und eben Ressourcen schonend ist.
    Auch wenn es manchmal nicht leicht ist.

    Und ich erwarte von niemanden sich den Code an zu gucken, lediglich um um Rat ob es möglich wäre.
    Es gibt genug die sich für dieses Thema interessieren und gerne auch ihren Senf dazugeben.
    Imperiums - Fortschritt in ein neues Zeitalter (Remake 2021)

    virus schrieb:

    Und ich erwarte von niemanden...
    Hab ich mich vlt. ungeschickt ausgedrückt.
    Ich meinte, auch ein Profi kann nicht 200 Zeile Code durchgucken, und dann nur vom gucken sagen "so und so".
    Allenfalls kann er dich auf zig Ungereimtheiten aufmerksam machen, und Stellen schlechten Stils auszubessern anregen, aber da kannste dich in sehr lange Diskussionen verzetteln, und am Ende mag dein Code technisch wesentlich besser sein, aber tut immer noch nicht, was du eiglich von ihm wolltest.
    Mein Statement (das in klammern) haste ja: Für deine Anforderung braucht man ein bidirektionales Befehlsprotokoll, und das ist in den 200 Zeilen bestimmt nicht drinne.
    Das mittm Befehlsprotokoll ist in meim Tut angesprochen und erklärt, worums dabei ühaupt geht, und afaik hat mein Tut damit ein Alleinstellungsmerkmal oder anders gesagt: Mir ist kein anderes Tut bekannt, was sich dieser Herausforderung stellt.

    Du kannst auch Stichwort Remoting oder besser noch "WCF" recherchieren - das sind Technologien, die dieses Problem auch lösen, wenn auch ganz anners als ich es getan hab.
    Also zu deinem Code..
    Der bietet aktuell keine Möglichkeit deine Anforderungen zu erfüllen. Außerdem ist er auch nicht Resourcenschonend und meiner Meinung nach ist er crap.
    Orientier dich an dem Tutorial was dir der EDR gepostet hat. Wenn dir das zu kompliziert ist dann stell explizite Fragen zu dem, was du nicht verstehst.

    LG
    Das ist meine Signatur und sie wird wunderbar sein!
    Hi
    ich verstehe schon, was das System da macht (und sogar sehr gut), aber es ist nicht elegant und einige Sachen gehören anders gelöst.

    Prinzipiell wäre es sinnvoll, ClientHandler über eine eindeutige ID durch ein Dictionary zuzuordnen, das wäre z.B. ein Benutzername oder sowas, was beide Benutzer kennen (man würde ggf. auch pro Client eine ID-Tabelle erstellen, die mit der des Clients übereinstimmt und diese dann auf eine global gültige übersetzen. Für die lokale ID des Benutzers x könnte man z.B. den Index von x in der nach unveränderlichen Kontonamen sortieren Kontaktliste wählen). Anschließend wird jeder ClientHandler seine eigenen Daten behandeln. Es gibt hierzu Arbeiterthreads, die jeden Client asynchron nacheinander durchlaufen (ist schon so) und, sofern Daten vorliegen, diese zu bearbeiten (Read auf den Stream). Außerdem wird nach jedem nicht erfolgreichen Read ein Timestamp gesetzt und der Zustand des Clients auf Pending gesetzt, bzw. der Client nach Ablauf des Timeouts auf IsAlive=False und damit aus der Liste der aktiven Clients gelöscht. Bis dahin wird darauf gewartet, dass sich der Client neu verbindet.
    Sendet ein Client Daten an einen anderen Client, identifiziert er den Ziel-Client in einem Header vor der Nachricht. Der Header muss dabei auch die Länge der Nachricht enthalten oder sie muss sich anders erschließen lassen.
    Der ClientHandler kennt dabei den ClientService und kann aktive Verbindungen ermitteln und schickt die Nachrichten an den über den Header ermittelten Zielclient.
    Und damit man einen eleganten Code erhält, macht man das am besten in einer Neuprogrammierung.

    Viele Grüße
    ~blaze~