Вопрос

I have looked just about all over Google, and even here on Stack overflow, but I cannot seem to find the solution I'm looking for. I am testing my programming skills remaking Pong using MonoGame for C# and I'm trying to make this multiplayer with both UDP Clients and a UDP Server. I'm going with "the perfect client-server model" idea where the server handles all the calculations, while the game client just receives data from the server and displays it on the screen. Unfortunately I have had past issue working with programming UDP Servers. I have a loop in which I receive a datagram, than begin listening for another. I use Asynchronous calls because in my mind that is what would work the best for client and server. Main code looks something like this: (I'm going to cut out the bit that won't affect CPU, and show only the networking.)

    static void Main()
    {
        //Initialize
        Console.Title = "Pong Server";
        Console.WriteLine("Pong Server");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        //Bind socket
        EndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 25565);
        Console.WriteLine("Binding to port 25565");
        serverSocket.Bind(localEndPoint);

        //Listen
        Console.WriteLine("Listening on {0}.", localEndPoint);

        //Prepare EndPoints
        EndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);

        //Recive Data...
        rcvPacket = new byte[Data.Size];
        serverSocket.BeginReceiveFrom(rcvPacket, 0, Data.Size, SocketFlags.None, ref clientEndPoint, new AsyncCallback(Receive), null);
    }

And then in the Receive(IAsyncResult ar) Method:

    static void Receive(IAsyncResult ar)
    {
        //Prepare EndPoints
        EndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);

        //End
        int PacketSize = serverSocket.EndReceiveFrom(ar, ref clientEndPoint);

        //<Handle Packet Code Here>

        //Receive Loop
        rcvPacket = new byte[Data.Size];
        serverSocket.BeginReceiveFrom(rcvPacket, 0, Data.Size, SocketFlags.None, ref clientEndPoint, new AsyncCallback(Receive), null);
    }

What this code looks like is that it will wait until a packet is received, than listen for another one, which will halt this "listener" thread, being asynchronous. Anyway thanks for reading my question and hopefully I'll get to the bottom of this.

Oh by the way, here is an image of one of my 4 cores getting maxed out. (Yes I'm currently debugging it.) Debugging Pong Server.exe; Maxing one of 4 CPU Cores

Это было полезно?

Решение

Adding Thread.Sleep(1) call to the update loop fixed not only the CPU usage in the original problem, but it also made the the code executed in the update loop behave better. inside the while(alive)}{ } where I keep track of the elapsed time and total time, I insert the sleep call.

The value seems to be needed at least 1.

0 CPU Usage!

    static void Main(string[] args)
    {
        //Initialize
        Console.Title = "Pong Server";
        Console.WriteLine("Pong Server");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        //Bind socket
        EndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 25565);
        Console.WriteLine("Binding to port 25565");
        serverSocket.Bind(localEndPoint);

        //Listen
        Console.WriteLine("Listening on {0}.", localEndPoint);

        //Prepare EndPoints
        EndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);

        //Recive Data...
        serverSocket.BeginReceiveFrom(rcvPacket, 0, Data.Size, SocketFlags.None, ref clientEndPoint, new AsyncCallback(ReceiveFrom), null);

        //Initialize TimeSpans
        DateTime Now = DateTime.Now;
        DateTime Start = Now;
        DateTime LastUpdate = Now;
        TimeSpan EllapsedTime;
        TimeSpan TotalTime;

        //Create Physics Objects
        Paddle1 = new Physics2D();
        Paddle2 = new Physics2D();
        Ball = new Physics2D();

        //Loop
        while (alive)
        {
            Now = DateTime.Now;
            TotalTime = Now - Start;
            EllapsedTime = Now - LastUpdate;
            LastUpdate = Now;
            Update(EllapsedTime, TotalTime);

            //Add Sleep to reduce CPU usage;
            Thread.Sleep(1);
        }

        //Press Any Key
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

Другие советы

From MSDN documentation:

Your callback method should invoke the EndReceiveFrom method. When your application calls BeginReceiveFrom, the system will use a separate thread to execute the specified callback method, and it will block on EndReceiveFrom until the Socket reads data or throws an exception. If you want the original thread to block after you call the BeginReceiveFrom method, use WaitHandle.WaitOne. Call the Set method on a T:System.Threading.ManualResetEvent in the callback method when you want the original thread to continue executing. For additional information on writing callback methods, see Callback Sample

From my understanding, you create the callback thread directly when you call BeginReceiveFrom. Because you do not use EndReceiveFrom, you callback thread is executed, creating another thread , again and again ...

Use EndReceiveFrom to wait for the whole packet to be read, then your callback will repeat and start reading again. Watch out for thread safety I have no idea how you manage your data here.

Looking at that post could help : Asynchronous TCP/UDP Server

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top