udp client/server performance
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 10 of 10

Thread: udp client/server performance

Hybrid View

  1. #1
    Join Date
    May 2009
    Posts
    9

    udp client/server performance

    Hi,

    I'm working on a UDP game-server for an xna game (no, i'm not interested in using the networking tools build in to xna). I have it working but i need ways to improve it's performance.

    currently i have my data sorted like this:
    Code:
     
    class clientInfo
        {
            public EndPoint endPoint; // client identifier, since udp is connectionless
    
            public int lastPacketReceived = 0; // sequence number of last packet received
            public int lastPacketSend = 0; // sequence number of last packet send
            
            public List<packetInfo> sendPackets = new List<packetInfo>();
        }
        class packetInfo
        {
            public int sequenceNumber; // the packets equence number
            public long sentTicks; // time when the packets was send
            public int retryCount; // how many times the packet was resend
            public byte[] packet; // the data in the packet
        }
    I have a a list<clientInfo> that represents my clients.

    The main thread of my server listens for data on a udp port and pases the received data to a thread in the ThreadPool where it get's processed. I also have another tread in the ThreadPool that handles the acknowledgement of packets that where send, resend packets when needed and drops clients when it has to try to many times.

    The problem with this structure is that i have to lock almost the entire threads because they are both working on the same list of clients and the same lists of packets (connecting and disconnecting clients, adding end removing send packets, increasing retry counts, etc..). I guess this makes the threating almost useless... How can i fix this? Is there a better way to organize the data to fix this problem or do i need to manage the threads differently?

    Another problem is that when there are many clients the server seems to start dropping packets. Is there a better way of listening for packets? I read somewhere that there may be a period between the EndReceiveFrom and the next BeginReceiveFrom when the program isn't listening. Maybe i'l have to make multiple threats listen for new data?

    I hope you guy's can help me with this, it seems to be rather hard to find information on this topic. If you need more info to help me i'll be happy to share. I didn't post the entire code because it's rather long a little messy ATM .

    thanks in advance,
    wieweet

  2. #2
    Join Date
    May 2007
    Posts
    1,546

    Re: udp client/server performance

    Whats your current performance and desired performance target? Have you identified this section of code as an actual bottle neck in performance or are you just guessing?
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

  3. #3
    Join Date
    May 2009
    Posts
    9

    Re: udp client/server performance

    On the local network the server can now serve 11 clients. When i connect a 12th client, all clients disconnect. I have no idea why, it seems to start losing a lot of packets and then it fails the connections. Also i have no idea how to debug this.

    Another problem i am having is when testing over the internet the acknowledgement system i have right now doesn't seem to be good enough. It's constantly dropping clients. It's currently checking for packets that have not been acknowledged every 100 milliseconds but this may be to fast depending on the latency of the clients connection.

    I would like to be able to support at least 16 or 20 clients.

    I need to find the bottlenecks in my code but since there are no exceptions and so much data is being send i have no idea how to do that.

    An article i followed to get the basics of my udp server done:
    http://msdn.microsoft.com/en-us/magazine/cc163648.aspx
    mentioned:
    Code:
    In my sample receiver code, there's a short span of time between acceptance of a packet and calling another BeginReceiveFrom. Even if this call were the first one in MessageReceivedCallback, there's a still short period when the app isn't listening. One improvement would be activating several instances of BeginReceiveFrom, each with a separate buffer to hold received packets.
    I tried working around this by starting a separate thread to deal with the incoming data but i don't think its enough. What do you think is the best way to solve this?

    here is a simplified version of my code:
    Code:
    class SocketPacket
        {
            public EndPoint endPoint = new IPEndPoint(0, 0);
            public int clientIndex;
            public byte[] dataBuffer = new byte[1024];
        }
    	static private Socket m_serverSocket;
            static private AsyncCallback onReceiveData = new AsyncCallback(OnReceiveData);
    
            static private List<clientInfo> clients = new List<clientInfo>();
            static readonly object clientLock = new object();
            private int m_port;
    
            private void startServer()
            {
                m_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    
                EndPoint bindEndPoint = new IPEndPoint(IPAddress.Any, m_port);
                m_serverSocket.Bind(bindEndPoint);
    
                ListenForData();
    
                ThreadPool.QueueUserWorkItem(CheckPendingAcks, 0);
                
            }
    
            public void CheckPendingAcks(object o)
            {
                while (true)
                {
                    Thread.Sleep(100);
                    try
                    {
                        lock (clientLock)
                        {
                            for (int i = 0; i < clients.Count; i++)
                            {
                                
                                // check packets pending acknowledgement, resend when needed.
    			    // disconnect client on to many retries
                            }
                        }
    
    
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("CheckPendingAcks error: " + e.ToString());
                    }
                }
            }
    
    
            static private void ListenForData()
            {
                SocketPacket packet = new SocketPacket();
                try
                {
                m_serverSocket.BeginReceiveFrom(packet.dataBuffer, 0, packet.dataBuffer.Length, SocketFlags.None, ref packet.endPoint, onReceiveData, packet);
                }
                catch (Exception e)
                {
                    Console.WriteLine("BeginReceiveFrom error: " + e.ToString());
                }
            }
    
            static private void OnReceiveData(IAsyncResult result)
            {
                SocketPacket socketPacket = (SocketPacket)result.AsyncState;
                int bytesRead = 0;
                try
                {
                    bytesRead = m_serverSocket.EndReceiveFrom(result, ref socketPacket.endPoint);
                    ThreadPool.QueueUserWorkItem(ProcessIncomingData, socketPacket);
                }
                catch (Exception e)
                {
                    Console.WriteLine("OnReceiveData error: " + e.ToString());
                }
                
                ListenForData();
            }
    
            static private void ProcessIncomingData(object obj)
            {
                try
                {
                    lock (clientLock)
                    {
                        SocketPacket packet = (SocketPacket)obj;
                        // ID client and procces incomming data
    		    // remove acknowledged packets
    		}
                }
                catch (Exception e)
                {
                    Console.WriteLine("ProcessIncomingData error: " + e.ToString());
                }
            }
    thanks,
    wieweet

  4. #4
    Join Date
    May 2009
    Posts
    9

    Re: udp client/server performance

    What if i make one thread per client that takes care of processing data and handling acknowledgement. Each tread would hold the data for a single client. This way i don't have to access the same list of clients all the time so i wont have to lock the threads. The code that listens for incoming data would have to pass this data to the correct thread. Is this a good idea?

    I still need to fix the span of time between acceptance of a packet and calling another BeginReceiveFrom.

    Any idea how to best solve this problem and how to then pass the received data to the desired thread?

  5. #5
    Join Date
    May 2007
    Posts
    1,546

    Re: udp client/server performance

    To be honest, get it working correctly with TCP first and then change to UDP later. Once your sure your core logic is correct and there are no major issues anymore you can change your transport from TCP to UDP and then deal with all those issues. UDP is very hard to get right unless you're pretty familiar with network programming to begin with.

    As it is, your listen for data loop is more or less ok, but you don't handle errors properly on the UDP sockets (see documentation). Basically if you get an error you either handle it or ignore it, but you must keep calling Receive if it's a 'normal' error. Instead once a single error is received you stop listening for more data.
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

  6. #6
    Join Date
    May 2009
    Posts
    9

    Re: udp client/server performance

    Hi Mutant_Fruit,
    I did start with tcp and am switching to udp because tcp isn't fast enoug. Over lan it works but over the internet it just won't work. (the game i'm making is a 2D top down shooter, data needs to be send fast to make the game playable) Could you give me a better explanation of the error handling? I think i'm (for now) just catching any error and displaying it or am i wrong? (it gives me the error and the line of code at witch it happens)

    The problem when switching form tcp to udp is the acknowledgement etc that i have to do myself now. I'm doing this in a separate thread (got that idea form the tutorial a mentioned above). This made me have a lot of errors because of accessing the same data in deferent threads, and that's why i locked them. Now, for some reason, the server cant handle more then 10-11 clients before it drops them all (localy) and over the internet it just drops the clients after a short period. Given that i don't get any error's my best guess is that it's dropping a lot of packets. (ATM i have, like in the tutorial, every packet retry 5 times. If here is no response after that, i fail the clients connection)

  7. #7
    Join Date
    May 2007
    Posts
    1,546

    Re: udp client/server performance

    The only reason for implementing ACK packets with UDP is that you care that they are delivered. If you care that they are delivered, you should just use TCP. What you've implemented is essentially a bad version of TCP so why not just use the highly optimised and bugfree TCP implementation that most of the world uses instead

    If you don't care if your packets are lost, i.e. if the next packet contains information which makes the previous one obsolete, use UDP. It can make sense in that situation. Either way, the odds of either TCP or UDP causing your slowdown are pretty tiny. It's something else in your application but you'd have to apply a profiler to find out.
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

  8. #8
    Join Date
    May 2009
    Posts
    9

    Re: udp client/server performance

    ok, so i'm somewhere in between I don't care if the updates for the players location etc all arrive. That just has to go fast. Things like hits and game begin and end they have to arrive and be reliable.

    When i was testing my solution over the internet there didn't seem to be much delay or data-loss when using udp, the game ran pretty good. But it keeps disconnecting.

    I don't know why my tpc solution was unusable when testing over the internet. Locally it ran great but over the internet it was unplayable. It had separate threads for every client with each there own socket and a master socket for new clients connecting. (it's easy with tcp because it keeps a connection automatically)

    It's possible there is something else is going wrong but i have no idea what that would be and how to find it. I can do basic debugging but since i now have multiple threads and the clients only disconnect when there is a lot of clients (lots of data flying by) it got almost impossible for me to find other exceptions.

  9. #9
    Join Date
    May 2007
    Posts
    1,546

    Re: udp client/server performance

    Things like hits and game begin and end they have to arrive and be reliable.
    Then UDP is not suitable unless you implement a TCP-like system on top of it. UDP does not guarantee delivery and it is 100% certain that packets will be dropped as you cross the internet. If you were experiencing issues with TCP then I can only assume the problem is elsewhere in your code. TCP is what the entire world runs on at blazing fast speeds with no issues
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

  10. #10
    Join Date
    May 2009
    Posts
    9

    Re: udp client/server performance

    ok, i'm not sure of i should continue here or make another topic.
    This is the tcp code i was using:

    Code:
    class TCPSocketServer
        {
            public AsyncCallback pfnWorkerCallBack;
    
            private Socket m_mainSocket;
    
            private List<Socket> m_workerSockets = new List<Socket>();
    
            private int m_port;
    
            private void startServer()
            {
                try
                {
                    m_mainSocket = new Socket(AddressFamily.InterNetwork,
                                              SocketType.Stream,
                                              ProtocolType.Tcp);
                    m_mainSocket.Bind(new IPEndPoint(IPAddress.Any, m_port));
    
                    m_mainSocket.Listen((int)SocketOptionName.MaxConnections);
    
                    m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
    
                }
                catch (SocketException se)
                {
                    Console.WriteLine(se.Message);
                }
            }
    
            private void OnClientConnect(IAsyncResult asyn)
            {
                try
                {
                    Socket workerSocket = m_mainSocket.EndAccept(asyn);
    
                    int nextClientCount = GetNextClientCount();
    
                    if (nextClientCount >= 0)
                    {
                        m_workerSockets.Insert(nextClientCount, workerSocket);
                    }
                    else
                    {
                        m_workerSockets.Add(workerSocket);
                    }
    
                    string msg = "Client " + m_workerSockets.IndexOf(workerSocket) + " Connected";
    
                    Console.WriteLine(msg);
    
                    WaitForData(workerSocket, m_workerSockets.IndexOf(workerSocket));
                    
                    m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
    
                }
                catch (ObjectDisposedException)
                {
                    Console.WriteLine("OnClientConnection: Socket has been closed");
                }
                catch (SocketException se)
                {
                    Console.WriteLine(se.Message);
                }
            }
    
            private class SocketPacket
            {
    
                public System.Net.Sockets.Socket m_currentSocket;
                public int m_clientIndex;
                public byte[] dataBuffer = new byte[1024];
    
                public SocketPacket(Socket socket, int clientIndex)
                {
                    m_currentSocket = socket;
                    m_clientIndex = clientIndex;
                }
            }
    
            private void WaitForData(Socket soc, int clientIndex)
            {
                try
                {
                    if (pfnWorkerCallBack == null)
                    {
                        pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
                    }
    
                    SocketPacket packet = new SocketPacket(soc, clientIndex);
    
                    soc.BeginReceive(packet.dataBuffer, 0,
                        packet.dataBuffer.Length,
                        SocketFlags.None,
                        pfnWorkerCallBack,
                        packet);
                }
                catch (SocketException se)
                {
                    Console.WriteLine(se.Message);
                }
            }
    
            public void OnDataReceived(IAsyncResult asyn)
            {
                SocketPacket socketData = (SocketPacket)asyn.AsyncState;
                try
                {
                    socketData.m_currentSocket.EndReceive(asyn);
    
                    // procces incoming data
    
                    WaitForData(socketData.m_currentSocket, socketData.m_clientIndex);
                }
                catch (ObjectDisposedException)
                {
                    Console.WriteLine("OnDataReceived: Socket has been closed");
                }
                catch (SocketException se)
                {
                    if (se.ErrorCode == 10054)
                    {
                        string msg = "Client " + socketData.m_clientIndex + " Disconnected";
                        Console.WriteLine(msg);
                        m_workerSockets[socketData.m_clientIndex] = null;
                    }
                    else
                    {
                        Console.WriteLine(se.Message);
                    }
                }
            }
    When i use if over the local network it works but when i tried it over the internet it was lagging a lot. (I didn't try how many clients it holds locally yet.) I switched to udp after this, i had already read that for fast paced games it would be better to use udp. I think the networking build in to xna olso uses udp.

    Can you see something i'm doing wrong? Do you need to see the client code to? Should i use tcp or udp?

    thanks,
    wieweet

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center