Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C# Game Server Networking
I've been working on this for a while (much longer than anticipated) and still can't seem to get a client-server working with C#. I think the main issue is my poor understanding of sockets in C# (I could implement this in Java no problem).
 
Any help finding my error(s) or helping me understand how to implement it properly would be appreciated. A code sample of how you would implement any one of my functions below would be great Smile | :)
 
The goal is to have multiple clients connected to a single server. Each client can send data updates/changes to the server, which then relays those updates to all other clients. This should be simple, right?
 
The server tracks all currently connected clients in a list along with the TcpClient object acquired from the connection.
 
The following code snippets are mostly entry points for threads and are supposed to do the following:
 
Server Side:
> Accept new clients
> Receive data from client
> Send data to clients
 
Client Side:
> Send data to server
> Receive data from sever
 
//Server Thread Entry Point: Waiting for client connections
        private void acceptClientConnection()
        {
            // Create a TCP/IP listener.
            TcpListener tcpListener = new TcpListener(IPAddress.Any, 3000);
            tcpListener.Start();
 
            //start listening for incomming connections from clients
            while (true)
            {
                //Wait for incoming client connection
                TcpClient client = tcpListener.AcceptTcpClient();
 
                //Save connection (launches new thread that waits for client msgs)
                clients.AddLast(new ConnClient(client, clients.Count));
 
                Console.WriteLine("SERVER: client connected to server");
            }
        }
No problems so far, but I'm pretty sure I have issues here: The following code is where the server handles in-coming data from the clients. (This is run in a thread; one thread per client). The changes received are saved in a queue and are processed by another thread.
 
I feel this code should block, and wait for a message from the client, but I don't think that is happening.
 
//Comm Thread Entry Point
            private void handleClientComm()
            {
                while (true)
                {
                    //handle incomming messages
                    if (conn.Connected)
                    {
                        Console.WriteLine("SERVER: still connected, reading stream");
                        //Get data from stream
                        byte[] data = ReadToEnd(conn.GetStream());
 
                        lock (dataChanges)
                        {
                            //add data to the change list
                            dataChanges.AddLast(new Pair<int, byte[]>(clientIndex, data));
 
                            //wake up any threads waiting on data
                            Monitor.PulseAll(dataChanges);
                        }
                    }
                    else Console.WriteLine("SERVER: not connected");
                }
            }
 
conn == the saved TcpClient object.
ReadToEnd(Stream) is a function I copied from a response to someone else question on here. I can't guarantee its correctness...
 
The following is the code that actually sends the updates to the clients:
I believe its working as intended.
 
//Synch Data Thread Entry Point
        private void synchData() 
        {
            while (true)
            {
                lock (dataChanges)
                {
                    //wait for data if there isn't any
                    Console.WriteLine("SERVER: waiting for data to synch");
                    while (dataChanges.Count == 0)
                        Monitor.Wait(dataChanges);
 
                    //send updates to all the clients
                    LinkedList<ConnClient>.Enumerator itr = clients.GetEnumerator();
                    for (int i = 0; i < clients.Count; i++ )
                    {
                        itr.MoveNext();
                        if(i == dataChanges.First.Value.index)
                            continue; // no need to send change data to the client that made the change

                        //send data to client
                        itr.Current.sendDataUpdate(dataChanges.First.Value.data);
                    }
                    dataChanges.RemoveFirst();
                    Console.WriteLine("SERVER: data change synched");
                }
            }
        }
and
//send data update to client (called from a separate thread)
            public void sendDataUpdate(byte[] data)
            {
                //handle incomming messages
                if (conn.Connected)
                {
                    //send data to the client
                    conn.GetStream().Write(data, 0, data.Length);
                    conn.GetStream().Flush();
                }
            }
 
The client is a little simpler, one thread for sending changes, one thread for receiving updates. If there is an issue here, I believe it will be while receiving updates - for the same reasons I think the server receiving data is wrong.
 
//make initial contact with the server
            server = new TcpClient();
            IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(hostIP), hostPort); //127.0.0.1 //3000
            server.Connect(serverEndPoint);
 
 
//send changes to server
        private void sendUpdates() 
        {
            while (true)
            {
                lock (dataChanges)
                {
                    //Wait until there is data to send
                    while (dataChanges.Count == 0)
                        Monitor.Wait(dataChanges);
 
                    //if still connected to server
                    if (server.Connected)
                    {
                        NetworkStream nStream = server.GetStream();
                        nStream.Write(dataChanges.First.Value, 0, dataChanges.First.Value.Length);
                        nStream.Flush();
 
                        dataChanges.RemoveFirst();
                        Console.WriteLine("CLIENT: data change sent to server");
                    }
                    else Console.WriteLine("CLIENT: no longer connected to server");
                }
            }
        }
 
//recieve updates from server
        private void receiveUpdates() 
        {
            while(true)
            {
                //if still connected to server
                if (server.Connected)
                {
                    //Get data from stream
                    Console.WriteLine("CLIENT: waiting for data from server");
                    byte[] data = ReadToEnd(server.GetStream());
 
                    //Deserialize change
                    AbstractDataChange dChange = aData.deserialize(data);
                    Console.WriteLine("CLIENT: recieved data update from the server");
 
                    //update synched data
                    lock (aData)
                    {
                        aData.updateData(dChange);
                    }
                }
                else Console.WriteLine("CLIENT: no longer connected to server");
            }   
        }
 
.
Posted 22-Dec-11 8:08am
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

If it were me, I would let the clients push their own updates and pull other client updates on a regular basis. That makes the server nothing more than a glorified data clearinghouse. All it does is listen for client connections, and then the clients drive the comm session - not the server.
  Permalink  
Comments
ProjectXC at 22-Dec-11 13:29pm
   
Alright, now that I have my issue resolved, your suggestion intrigues me. I may implement it and see if it runs more efficiently for my given scenario.
 
I don't know if I like it yet though... The clients will all be sitting there spinning, constantly asking for updates instead of just waiting to receive them.
 
My solution blocks the server's "send data" thread when there is no data to send.
John Simmons / outlaw programmer at 22-Dec-11 14:27pm
   
The clients don't have to poll constantly (every N milliseconds), and they would only pull down data that's been updated, and maybe even only data for other clients that are "visible" to them (or within a pre-defined visibility/audible interaction zone).
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

Alright, my question has been solved. I removed the
ReadToEnd(conn.GetStream());
function call, and replaced it with
conn.GetStream().Read(data, 0, buffSize); 
My client-server is functioning correctly, now I just need to figure out how to send messages of arbitrary length...
 
This is a good example of "Don't blindly copy-paste!!" The function I used did not work as expected!
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
0 OriginalGriff 8,284
1 Sergey Alexandrovich Kryukov 7,407
2 DamithSL 5,614
3 Maciej Los 4,989
4 Manas Bhardwaj 4,986


Advertise | Privacy | Mobile
Web03 | 2.8.1411023.1 | Last Updated 22 Dec 2011
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100