高流量C#UDP网络

时间:2011-09-13 09:19:47

标签: c# networking xna

我制作的游戏需要大量的数据流量,如位置(2D)和许多其他数据。 我使用一个非常简单的类来帮助我监听8080端口(UDP),以及一个发送数据报的方法:

public static void SendToHostUDP(string Msg)
{
    UdpClient udpClient = new UdpClient();
    udpClient.Connect(Main.HostIP, 8080);
    byte[] sdBytes = Encoding.ASCII.GetBytes(Msg);
    udpClient.BeginSend(sdBytes, sdBytes.Length, CallBack, udpClient);
    Main.UDPout += sdBytes.Length / 1000f;
}
public static void SendToClientUDP(string Msg, IPAddress ip)
{
    UdpClient udpClient = new UdpClient();
    udpClient.Connect(ip, 8080);
    byte[] sdBytes = Encoding.ASCII.GetBytes(Msg);
    udpClient.BeginSend(sdBytes, sdBytes.Length, CallBack, udpClient);
    Main.UDPout += sdBytes.Length / 1000f;
}
public static void CallBack(IAsyncResult ar)
{

}

监听器类只是一个非常简单的类:

public class NetReciever
{
    public TcpListener tcpListener;
    public Thread listenThread;
    private Action actionToPerformTCP;
    private Action actionToPerformUDP;
    public UdpClient udpClient;
    public Thread UDPThread;
    TimerAction UDPPacketsCounter;
    int UDPPacketsCounts;
    private BackgroundWorker bkgUDPListener;
    string msg;
    public NetReciever(IPAddress IP)
    {
        this.tcpListener = new TcpListener(IPAddress.Any, 25565);
        this.udpClient = new UdpClient(8080);
        this.UDPThread = new Thread(new ThreadStart(UDPListen));
        this.listenThread = new Thread(new ThreadStart(ListenForClients));
        this.listenThread.Start();
        UDPPacketsCounter = new TimerAction(CountUDPPackets, 1000, false);
        this.UDPThread.Start();
    }
    public void CountUDPPackets()
    {
        UDPPacketsCounts = 0;
    }
    public void Abort()
    {
        UDPThread.Abort();
        udpClient.Close();
        listenThread.Abort();
        tcpListener.Stop();
    }
    public void UDPListen()
    {
        while (true)
        {
            IPEndPoint RemoteIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
            byte[] receiveBytesUDP = udpClient.Receive(ref RemoteIPEndPoint);
            if (receiveBytesUDP != null)
            {
                UDPPacketsCounts++;
                Main.UDPin += receiveBytesUDP.Length / 1000f;
            }
            if (Main.isHost)
            {
                Main.Server.processUDP(Encoding.ASCII.GetString(receiveBytesUDP), RemoteIPEndPoint.Address.ToString());
            }
            else
            {
                if (RemoteIPEndPoint.Address.ToString() == Main.HostIP)
                {
                    Program.game.ProcessUDP(Encoding.ASCII.GetString(receiveBytesUDP));
                }
            }
        }
    }

所以基本上当有1个播放器时,会有大约60个分组的IN和60个分组/秒。

它的行为如下:

  1. 收听数据包。
  2. 验证数据包。
  3. 处理分组数据 - >就像存储一些位置一样,发回一些数据包......
  4. 所以它就像这样循环。

    问题在于:

    首先,当有2个玩家(Host + 1Client)时,某些时候会有一些重要的FPS掉落,并且主机会在一段时间内遇到所有音频(如蓝屏)的口吃。

    其次,当有2个以上的玩家(主机+ 1 +客户端)时,主机FPS将降至5-20,并且它将滞后+滞后+滞后+滞后但不会冻结。

    我已经阅读了一些关于异步的文章,这已经是线程化了吗?

    还有BeginRecieveEndRecieve,我真的不明白我是如何以及为什么需要使用它。

    有人可以提供一些示例来解释如何处理这些类型的数据并发送/接收数据包吗?我真的不想使用库,因为我想知道发生了什么。

    P.S:Terraria的网络系统如何运作?它使用TCP,但它很流畅!怎么样?
    PSS:什么是缓冲?为什么我需要设置缓冲以及如何设置?它有什么变化?
    PSSS:我认为在发送数据包时需要调整和更改,因为它看起来很简单。

2 个答案:

答案 0 :(得分:1)

异步概念肯定是你想要在这里看到的东西。可能的问题是,一切都在同一个线程上运行,某些UI动作(如图形渲染(你的fps丢失),或播放声音(你的口吃))可能正在等待程序的其他方面,如网络的通信。

通常情况下,你会将线程分开,以便事物的UI和声音方面可以自己处理,而不依赖于其他任何东西。阅读一些msdn线程示例,然后尝试将更长时间运行的进程放在UI的单独线程上,看看它有何帮助:

http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

答案 1 :(得分:1)

如果您真的想要创建一个联网游戏,那么就无法了解到目前为止您所了解的有关网络编程的更多信息。

一个好的开始是http://gafferongames.com/networking-for-game-programmers/sending-and-receiving-packets/。虽然这是针对C ++的(我想,但是如果我记得正确的人将它传递给C#,也许是:P)这一点背后的理论得到了很好的解释。

也许值得阅读WinAPI套接字编程。这将比阅读有关如何在C#中进行网络编程的教程更具技术性,但它也会比使用模糊了幕后真实情况的包装更清晰。

编辑:

基本上由您决定使用backgroud线程来监听数据包或使用AsyncCallback方法使用BeginReceive。后者的缺点是你最终需要调用EndReceive,此时它仍会阻止你的应用程序直到实际的接收完成。创建自己的线程并使用阻塞模式显然不会阻止您的UI /业务逻辑(主)线程,但您需要自己编写跨线程通信部分。

我还找到了一个使用线程和阻塞模式here的UDP-Client-Server应用程序的简单教程。