【问题标题】:High-Traffic C# UDP Networking高流量 C# UDP 网络
【发布时间】:2011-09-14 19:38:16
【问题描述】:

我正在制作一款需要大量数据流量的游戏,例如 Positions(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 个玩家时,大约 60packet/s IN 和 60packet/s out。

它的作用是这样的:

  1. 监听数据包。
  2. 验证数据包。
  3. 处理数据包数据 -> 就像存储一些位置,发回一些数据包......

所以它只是像这样循环。

问题就在这里:

首先,当有 2 个玩家(主机 + 1 个客户端)时,在某些时候会出现一些显着的 FPS 下降,并且主机会经历片刻所有音频的卡顿(比如蓝屏)。

其次,当有2+玩家(Host + 1+Client)时,Host FPS会下降到5-20,并且会lag+lag+lag+lag但不会卡顿。

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

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

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

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

【问题讨论】:

  • “赢得比赛的唯一方法就是根本不玩。”您不应该用大量数据淹没网络 - 仅在值实际发生变化时发送数据。此外,使用速率限制和客户端预测来避免同样的问题。老实说,在 Google 上花 2 分钟就会告诉你这一点 - 并给你举个例子。
  • 为什么你的回调是空的?至少你应该调用udpClient.EndSend 来完成异步操作。这和你昨天问的问题基本一样吗?
  • 什么是ListenForClients?它不在您的代码中。

标签: c# networking xna


【解决方案1】:

异步概念绝对是您想在这里研究的东西。可能的问题是,由于所有内容都在同一个线程上运行,某些 UI 操作(如图形渲染(您的 fps 损失)或播放声音(您的口吃))很可能正在等待程序的其他方面,例如网络通讯。

通常,您会将线程分开,以便 UI 和声音方面可以自行处理,而不依赖于其他任何东西。阅读一些 msdn 线程示例,然后尝试将运行时间较长的进程放在与 UI 不同的线程上,看看会有什么帮助:

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

【讨论】:

  • 非常感谢!声音卡顿是全球性的,当我使用iTunes或Skype时,我开始游戏时也会出现卡顿,直到游戏关闭后大约1分钟,这只是我的电脑问题吗?谢谢!所以基本上我在我的服务器类上开始一个新线程,那会更好吗?并且还使用 beginrecieve 而不是 recieve?
  • 但是,有没有我可以效仿的例子?再次感谢!
【解决方案2】:

如果您真的想创建一款网络游戏,那么您将无法学习比您目前所知道的更多的网络编程知识。

http://gafferongames.com/networking-for-game-programmers/sending-and-receiving-packets/ 是一个好的开始。虽然这是针对 C++ 的(我认为,但如果我没记错的话,也有人将它移植到 C#,也许 :P)这一切背后的理论都得到了很好的解释。

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

编辑:

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

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

【讨论】:

  • 我已经阅读了这些文章,但我仍然找不到我的问题,我是使用线程来监听 UDP 数据包(阻塞模式)还是只是 BeginRecieve?谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多