Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[C#] Help with Socket/Tcp

Newbie Spellweaver
Joined
Jan 12, 2016
Messages
10
Reaction score
1
So I'm new to network programming in C# and Im kinda stuck right now. So my code works perfectly for sending some data over from client to server and back from the server to client, but what if I want to keep the TcpClient open and send data from client to server later on? Or maybe even send data from server to client when connection is open?
Also, in order for the server to recieve data from client (if the TcpClient connection is open), does it need to be in a infinite loop? And how does the server check if theres data to recieve?

However, this is my code:

Server:
Code:
Console.WriteLine("Connecting...");
            TcpListener Listener = new TcpListener(IPAddress.Any, 30010);
            Listener.Start();
            Console.WriteLine("Connected...");


            while (true)
            {
                Console.WriteLine("Waiting for Client...");
                TcpClient Client = Listener.AcceptTcpClient();
                Console.WriteLine("Client accepted...");


                NetworkStream networkStream = Client.GetStream();
                byte[] buffer = new byte[1024];
                networkStream.Read(buffer, 0, buffer.Length);
                Console.WriteLine(Encoding.ASCII.GetString(buffer));


                byte[] sbuffer = Encoding.ASCII.GetBytes("MSG from Server...");
                networkStream.Write(sbuffer, 0, sbuffer.Length);
                networkStream.Close();
                Client.Close();
            }

Client:
Code:
Console.WriteLine("Connecting...");
                TcpClient Client = new TcpClient("127.0.0.1", 30010);
                Console.WriteLine("Connected to Server...");


                NetworkStream networkStream = Client.GetStream();
                byte[] buffer = Encoding.ASCII.GetBytes("Test MSG.");
                networkStream.Write(buffer, 0, buffer.Length);


                byte[] gbuffer = new byte[1024];
                networkStream.Read(gbuffer, 0, gbuffer.Length);
                Console.WriteLine(Encoding.ASCII.GetString(gbuffer));


                networkStream.Close();
                Client.Close();


                Console.ReadKey();
 
Last edited:
◝(⁰▿⁰)◜Smile◝ (⁰▿⁰)◜
Developer
Joined
May 29, 2007
Messages
2,167
Reaction score
899
There are a few approaches you can do. You can start doing async handeling of the socket so you keep it open. On accept of the connection you basically move the socket to it's own connection class where you start receiving with callbacks which will be triggered in the 'background' while they wait for data.

Here is an example:

Code:
    public abstract class Connection
    {
        private byte[] buffer = new byte[0x2000];
        private Socket socket;
        private bool isDisconnected = false;


        private string receivedString = string.Empty;


        public Connection(Socket socket)
        {
            this.socket = socket;


            // Start receiving data.
            this.socket.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveData), null);


        }


        private void OnReceiveData(IAsyncResult iAr)
        {
            try
            {
                int receivedLength = socket.EndReceive(iAr);


                if (receivedLength <= 0)
                {
                    Disconnect();
                    return;
                }


                // We have received some data.
                byte[] packetBuffer = new byte[receivedLength];
                Array.Copy(buffer, 0, packetBuffer, 0, packetBuffer.Length);


                // packetBuffer is the message we should have received, but it might be split up.
                receivedString += Encoding.ASCII.GetString(packetBuffer);


                List<string> packetsList = new List<string>();
                string[] packets;


                // Let's process the messages.
                if (receivedString[receivedString.Length - 1] == '\n')
                {
                    packets = receivedString.Substring(0, receivedString.Length - 1).Split('\n');


                    // We are going to store the packets in a list.
                    foreach (string packet in packets)
                    {
                        if (!string.IsNullOrWhiteSpace(packet))
                        {
                            packetsList.Add(packet); // Received a new packet.
                        }
                    }


                }
                else
                {
                    packets = receivedString.Split('\n');
                    int count = packets.Length;


                    // We are going to store the packets in a list.
                    for (int i = 0; i < (count - 1); i++)
                    {
                        if (!string.IsNullOrWhiteSpace(packets[i]))
                        {
                            packetsList.Add(packets[i]); // Received a new packet.
                        }
                    }


                    // Remember the incomplete message so we add the rest in the next check.
                    receivedString = packets[count - 1];
                }


                // We have our packets in the packet list, we now have to forward it the handlers.
                OnReceivedPackets(packetsList.ToArray());


                // Start receiving data.
                this.socket.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveData), null);


            }
            catch { Disconnect(); }
        }


        public void Send(string message)
        {
            Send(Encoding.ASCII.GetBytes(message + " \n"));
        }


        public void Send(byte[] buffer)
        {
            try { socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallback), null); }
            catch { Disconnect(); }
        }


        private void SendCallback(IAsyncResult iAr)
        {
            try { socket.EndSend(iAr); }
            catch { Disconnect(); }
        }


        public void Disconnect()
        {
            if (isDisconnected) return;
            isDisconnected = true;


            // We notify other users that the user is disconnected.
            OnDisconnect();


            // We close the socket.
            try { socket.Close(); }
            catch { }
        }


        protected abstract void OnReceivedPackets(string[] packets);
        public abstract void OnDisconnect();


    }
 
Last edited:
Newbie Spellweaver
Joined
Jan 12, 2016
Messages
10
Reaction score
1
There are a few approaches you can do. You can start doing async handeling of the socket so you keep it open. On accept of the packet you basically move the socket to it's own connection class where you start receiving with call backs which will be triggered in the 'background' while they wait for data.

Here is an example:

Code:
    public abstract class Connection
    {
        private byte[] buffer = new byte[0x2000];
        private Socket socket;
        private bool isDisconnected = false;


        private string receivedString = string.Empty;


        public Connection(Socket socket)
        {
            this.socket = socket;


            // Start receiving data.
            this.socket.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveData), null);


        }


        private void OnReceiveData(IAsyncResult iAr)
        {
            try
            {
                int receivedLength = socket.EndReceive(iAr);


                if (receivedLength <= 0)
                {
                    Disconnect();
                    return;
                }


                // We have received some data.
                byte[] packetBuffer = new byte[receivedLength];
                Array.Copy(buffer, 0, packetBuffer, 0, packetBuffer.Length);


                // packetBuffer is the message we should have received, but it might be split up.
                receivedString += Encoding.ASCII.GetString(packetBuffer);


                List<string> packetsList = new List<string>();
                string[] packets;


                // Let's process the messages.
                if (receivedString[receivedString.Length - 1] == '\n')
                {
                    packets = receivedString.Substring(0, receivedString.Length - 1).Split('\n');


                    // We are going to store the packets in a list.
                    foreach (string packet in packets)
                    {
                        if (!string.IsNullOrWhiteSpace(packet))
                        {
                            packetsList.Add(packet); // Received a new packet.
                        }
                    }


                }
                else
                {
                    packets = receivedString.Split('\n');
                    int count = packets.Length;


                    // We are going to store the packets in a list.
                    for (int i = 0; i < (count - 1); i++)
                    {
                        if (!string.IsNullOrWhiteSpace(packets[i]))
                        {
                            packetsList.Add(packets[i]); // Received a new packet.
                        }
                    }


                    // Remember the incomplete message so we add the rest in the next check.
                    receivedString = packets[count - 1];
                }


                // We have our packets in the packet list, we now have to forward it the handlers.
                OnReceivedPackets(packetsList.ToArray());


                // Start receiving data.
                this.socket.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveData), null);


            }
            catch { Disconnect(); }
        }


        public void Send(string message)
        {
            Send(Encoding.ASCII.GetBytes(message + " \n"));
        }


        public void Send(byte[] buffer)
        {
            try { socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallback), null); }
            catch { Disconnect(); }
        }


        private void SendCallback(IAsyncResult iAr)
        {
            try { socket.EndSend(iAr); }
            catch { Disconnect(); }
        }


        public void Disconnect()
        {
            if (isDisconnected) return;
            isDisconnected = true;


            // We notify other users that the user is disconnected.
            OnDisconnect();


            // We close the socket.
            try { socket.Close(); }
            catch { }
        }


        protected abstract void OnReceivedPackets(string[] packets);
        public abstract void OnDisconnect();


    }
That seems complicated!
I don't really understand what Async is tbh, but I'm gonna for sure read about it right away. Thanks for the fast reply!
 
Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
When you're using async, on the low level your program is in an infinite event loop. This is transparent to you for the most part. OnReceiveData() will not be called immediately- and code written after it will happily execute many microseconds before OnReceiveData will have a chance to. You aren't quite subject to race conditions like you would be with threading- and that's a good thing. When the socket.BeginReceive is called, it will set up such an event loop in the background of your program, and wait for data from the other side. When data is completely received, BeginReceive will immediately trigger OnReceiveData, passing it that data, and continue to wait for more data- where it will do the same thing (hence the term, event-loop- an event happens, and a callback is triggered).

It should also be noted that the 'queue' in an event-loop is executed synchronously. If you were to put another BeginReceive after the first one, the callback should be triggered after the first one is finished executing (not executed parallel like threads would be)- but both should be triggered unless something in the first one prevents the second from being triggered - ie a fatal error, or something in the first callback that breaks the event-loop.

Though, I don't know if multiple listeners on the same event is legal in C#, I know enough to know it's a bad idea. One pattern is to have one callback that determines what the data is regarding- and delegate data targeted towards a given task to one of several task handler functions, or . For example, if you can receive data for slaughtering cows, and data for slaughtering chickens, you can use the callback to determine which of those 2 possibilities the client chose, and pick one of 2 functions based on something the function expects to see (SlaughterCow, or SlaughterChicken). And if the data is compatible with neither, than handle an invalid packet accordingly. Of course, this assumes your socket server will be for more than a single use-case, and will receive several channels of data- a single or handful of plugins is useless. They aren't helping you unless you have more than 5.
 
Last edited:
Back
Top