Zellpop Posted April 7, 2013 Share Posted April 7, 2013 I've searched the great Internet for a TCP server that handles multiple connections. And everyone uses a separate thread for each connection. After some trial and errors I think I've found a solution that uses two threads, where the second handles the communication. Some information before I start. Have been using VB.Net since its my «native» language For C# user it should be fairly easy to follow. When it comes to other languages I hope it will nudge you in the right direction. The .Net version is 4.5, but I think its more or less the same down to 2.0 Since VB.Net doesn't use a C language style/naming, I will try to translate some of the words to the C style syntax. Module = Static Class Sub = A routine/method that doesn't return a value Function = A routine/method that return a value Dim = A word used to declare a variable (Dim MyString as String) ReDim = resize an array New = The constructor key word (Dim MyCar as New Car) Dictionary(of Key, Value) = Strongly typed HashTable Queue(of Value) = Strongly typed Queue Imports = Using/Include... Namespace = I think that is the same all over the place CInt = Convert to Int32 CUInt = Convert to UInt32 Don't be alarmed about _Class naming. Its my way of naming Classes Here Is my code: First we make a class that holds all information about the connected user/client. Imports System.Net Imports System.Net.Sockets Namespace Network Public Class Client_Class Public MySocket As Socket Public SocketID As UInt32 End Class End Namespace MySocket holds all information about the connection, and SocketID is used by my/your code to find the «right» connection to send/receive bytes. The server need a way to store the incoming/outgoing bytes along with the ID of Sender/Receiver. So you'll need this message class Namespace Network Public Class Message_Class Private _ID As UInt32 Private _Message() As Byte Public Sub New(ID As UInt32, Message() As Byte) _ID = ID _Message = Message End Sub Public ReadOnly Property ID As UInt32 Get Return _ID End Get End Property Public ReadOnly Property Message As Byte() Get Return _Message End Get End Property End Class End Namespace As you can see the key word End is all over the place. It represent the end curly braces } This class has two fields, two properties and a constructor That was easy, now the hard part. Doing section by section I will try to explain what is going on. Imports System.Threading Imports System.Net Imports System.Net.Sockets Namespace Network Public Module ConnectionMGR Private _ConnectionMGRThread As New Thread(AddressOf ConnectionLoop) Private _ListOfConnections As New Dictionary(Of UInt32, Client_Class) Private _SendQueue As New Queue(Of Message_Class) Private _serverIP As IPAddress = IPAddress.Parse("127.0.0.1") Private _ServerPort As UInt16 = 5000 Private _ServerConnection As TcpListener Private _SocketID As UInt32 = 0 _ConnectionMGRThread is the thread that handles all the TCP communication, and starts in the Sub ConnectionLoop _ListOfConnections stores all connections as Key Value pair in a Dictionary (strongly typed Hastable) _SendQueue holds all messages that is going to be sent as Message_Class we made earlier. _ServerConnection is the mother of all the Sockets used. _SocketID is the unique value so we can find the correct client(will increase for each connection) Public Sub Start() _ConnectionMGRThread.Start() End Sub Public Sub Send(Message As Message_Class) _SendQueue.Enqueue(Message) End Sub Private Function NewSocketID() As UInt32 _SocketID += CUInt(1) Return _SocketID End Function Start starts the communication thread Send puts the message in the send queue. This should be thread safe since only one thread Peeks/Dequeues the queue NewSocketID returns +1 each time Private Sub NewConnections() Dim Client As New Client_Class Client.SocketID = NewSocketID() Client.MySocket = _ServerConnection.AcceptSocket _ListOfConnections.Add(Client.SocketID, Client) End Sub NewConnections is called every time a new connection is about to be established. Then we create a new Client instance of the Client_Class. Then we set the public fields .SocketID and .MySocket. Then we add it to _ListOfConnections Private Sub ConnectionLoop() Console.WriteLine("Communication Loop is listening") _ServerConnection = New TcpListener(_serverIP, _ServerPort) _ServerConnection.Start() Dim ReceiveBuffer() As Byte Dim ReceivedBytes As Integer Do While True If _ServerConnection.Pending() Then NewConnections() End If For Each Client As Client_Class In _ListOfConnections.Values If Client.MySocket.Available > 0 Then ReDim ReceiveBuffer(Client.MySocket.Available - 1) ReceivedBytes = Client.MySocket.Receive(ReceiveBuffer, ReceiveBuffer.Length, 0) Dim NewMessage As New Message_Class(Client.SocketID, ReceiveBuffer) Crypto.DeCryptMGR.DeCrypt(NewMessage) End If Next Do While _SendQueue.Count > 0 Dim SendClient As Client_Class Dim SendMessage As Message_Class SendMessage = _SendQueue.Dequeue() SendClient = _ListOfConnections.Item(SendMessage.ID) SendClient.MySocket.Send(SendMessage.Message, 0, SendMessage.Message.Length, SocketFlags.None) Loop Thread.Sleep(1) Loop End Sub End Module End Namespace When the thread is started, it begins in this Sub. All handling of any TCP/sockets is done by this thread. Hopefully it will keep it thread safe. ReciveBuffer is the array of bytes form incoming connections is stored. RecivedBytes is not in use here, but stores the amount of bytes recived. Now we enter the Loop that will never stop since True is always True. The first IF statement checks if someone is trying to connect For Each iterates through every client in the ListOfConnections to check if they have any incoming messages. If there is, resize the ReceiveBuffer to fit the new message. Create a new Message_Class and send it where you want. The code sends it to Crypto..... Do While checks if the _SendQueue contains any messages to send. If it does finds the correct Client and sends it of and does this until the _SendQueue.Count = 0. Then we Sleep for 1 millisecond and starts all over again. As you might have noticed, there are no error checking here. I was going for a minimalistic code example. But remember Murphy's law «everything that can go wrong will go wrong» This example doesn't take into account that connections might be slow/faulty/disconnected or otherwise broken. So to start everything up Module StartupSequence Sub Main() Network.ConnectionMGR.Start() Dim Message As Network.Message_Class Dim clientID As UInt32 Do While True clientID = CUInt(Console.ReadLine()) Message = New Network.Message_Class(clientID, {97, 95, 96, 93, 91, 100}) Network.ConnectionMGR.Send(Message) Loop End Sub End Module Sub Main() is where everything starts. The first client connected gets ID 1 next 2 and so on, in the console window you can write the ID you want to send to, and the client will receive the Array of Bytes {97, 95, 96, 93, 91, 100} Hope you will find it useful and create your own version in different languages. And share them with the non VB.Net users Theoretically this server could have 4.3 bill connections, but somehow I think its gonna meet the max long before that ( memory, network buffers, time for each iterations) Zellpop 1 Quote Link to comment Share on other sites More sharing options...
Andrei Posted May 29, 2017 Share Posted May 29, 2017 Hi I tryed your code but Crypto is not defined anywhere Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.