我制作的游戏需要大量的数据流量,如位置(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个分组/秒。
它的行为如下:
所以它就像这样循环。
问题在于:
首先,当有2个玩家(Host + 1Client)时,某些时候会有一些重要的FPS掉落,并且主机会在一段时间内遇到所有音频(如蓝屏)的口吃。
其次,当有2个以上的玩家(主机+ 1 +客户端)时,主机FPS将降至5-20,并且它将滞后+滞后+滞后+滞后但不会冻结。
我已经阅读了一些关于异步的文章,这已经是线程化了吗?
还有BeginRecieve
和EndRecieve
,我真的不明白我是如何以及为什么需要使用它。
有人可以提供一些示例来解释如何处理这些类型的数据并发送/接收数据包吗?我真的不想使用库,因为我想知道发生了什么。
P.S:Terraria的网络系统如何运作?它使用TCP,但它很流畅!怎么样?答案 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应用程序的简单教程。