高流量 C# UDP 网络
我正在制作一款需要大量数据流量的游戏,例如位置(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 个数据包/秒输入和输出 60 个数据包/秒。
它的行为如下:
- 监听数据包。
- 验证数据包。
- 处理数据包数据->就像存储一些位置,发回一些数据包......
所以它就这样循环。
问题就在这里:
首先,当有2个玩家(主机+1个客户端)时,在某些时候会出现一些明显的FPS下降,并且主机会出现所有音频卡顿(如蓝屏)的情况。
其次,当有2个以上玩家(主机+1+客户端)时,主机FPS会下降到5-20,并且会卡顿+卡顿+卡顿+卡顿但不卡顿。
我读过一些关于异步的文章,这已经是线程化的了?
还有 BeginRecieve
和 EndRecieve
,我不太明白如何以及为什么需要使用它。
有人可以提供一些例子来解释如何处理这些类型的数据并发送/接收数据包吗?我真的不想使用库,因为我想知道发生了什么。
PS:泰拉瑞亚中的网络系统是如何运作的?虽然使用TCP但是很流畅!怎么办?
PSS:什么是缓冲?为什么需要设置缓冲以及如何设置?它改变了什么?
PSSS:我认为发送数据包时有一些需要调整和改变的地方,因为它看起来很简单。
I am making a game which demands great traffic of data like Positions(2D) and many other data.
I am using a very simple class to help me listening on 8080 port(UDP), and a method to send datagram :
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)
{
}
The listener class is just a very simple one:
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));
}
}
}
}
So basically when there is 1 player, there will be approximately 60packet/s IN and 60 packets/s out.
It acts like this:
- Listen for packets.
- Validate the packets.
- Process the packets data -> Like storing some of the positions, sending back some packets...
So it just loop like this.
And the problems are here:
Firstly, When there are 2 players(Host+ 1Client), there will be some significant FPS drops at some point, and the host will experience stuttering of all audios(like in blue screen) for a moment.
Secondly, when there are 2+ players(Host + 1+Client), the Host FPS will drop to 5-20, and it will lag+lag+lag+lag but not freezing.
I have read some articles about async, and this is already threaded?
And also BeginRecieve
and EndRecieve
, I don't really understand how and why do I need to use it.
Could someonekindly provide some examples to explain how to process these kinds of data and send/recieve packets please? I don't really want to use libraries because I want to know what is going on.
P.S: How do the networking system in Terraria works? It uses TCP but it is smooth! How?
PSS: What is buffering? Why do i need to set buffering and how? What does it changes?
PSSS: I think there are something to be tuned and changed in sending the packets, because it just look so simple.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
异步概念绝对是您想要在这里研究的内容。可能的问题是,当所有内容都在同一线程上运行时,某些 UI 操作(例如图形渲染(您的 fps 损失)或播放声音(您的口吃))很可能正在等待程序的其他方面,例如网络通讯。
通常,您会将线程分开,以便 UI 和声音方面可以自行处理,而不依赖于其他任何东西。阅读一些 msdn 线程示例,然后尝试将运行时间较长的进程放在 UI 之外的单独线程上,看看这有何帮助:
http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx
The asyncronous concept is definitely something you want to look into here. What could be the issue is that with everything running on the same thread, certain UI actions (like graphic rendering (your fps loss), or playing sound (your stuttering)) may well be waiting for other aspects of the program, such as network communications.
Normally, you'd seperate the threads out, so that the UI and sound side of things can process on their own, without the dependence on anything else. Have a read of some of the msdn thread examples, then try putting your longer running processes on a seperate thread from your UI and see how that helps:
http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx
如果您真的想创建一个网络游戏,那么您就无法学习更多有关网络编程的知识。
一个好的开始是 http://gafferongames.com/networking-对于游戏程序员/发送和接收数据包/。虽然这是针对 C++ 的(我想,但如果我没记错的话,有人也将其移植到 C# 上,也许 :P),这一切背后的理论都得到了很好的解释。
WinAPI 套接字编程也可能值得阅读。这比阅读有关如何使用 C# 进行网络编程的教程更具技术性,但它也比使用混淆幕后实际情况的包装器使事情更加清晰。
编辑:
基本上取决于您是否使用后台线程来侦听数据包或将 BeginReceive 与 AsyncCallback 方法一起使用。后者的缺点是您最终需要调用 EndReceive,此时它仍然会阻止您的应用程序,直到实际接收完成。创建自己的线程并使用阻塞模式显然不会阻塞您的 UI/业务逻辑(主)线程,但您需要自己编写跨线程通信部分。
我还找到了使用线程和阻塞模式的 UDP 客户端服务器应用程序的简单教程 此处。
If you truly want to create a networked game there will be no way around learning more about network programming than you seem to know so far.
A good start is http://gafferongames.com/networking-for-game-programmers/sending-and-receiving-packets/. Whilst this is for C++ (I think, but if I remember right someone portet it to C# also, maybe :P) the theory behind all this is explained very well.
It might also be worth reading up on WinAPI socket programming. This will be more technical than reading tutorials on how to do network programming in C#, but it will also make things more clear than using wrappers that obfuscate whats really going on behind the scenes.
Edit:
Basically its up to you weather you use a backgroud thread for listening for packets or use BeginReceive with an AsyncCallback method. The drawback of the latter is that you will eventually need to call EndReceive at which point it will still block you application until the actual receive is finished. Creating your own thread and using blocking mode will obviously not block your UI/business logic (main) thread, but you will need to program the cross thread communication part yourself.
I also found a simple tutorial for an UDP-Client-Server app using threading and blocking mode here.