我正在与C#和XNA进行一场小型在线多人乒乓球比赛。
我使用套接字在我个人LAN上的两台计算机之间传输数据。它工作正常。
问题在于速度:传输速度很慢。
当我ping第二台计算机时,它显示2 ms的延迟。 我在我的代码中设置了一个小计时器,它显示了大约200毫秒的延迟。 即使服务器和客户端在同一台计算机上(使用127.0.0.1),延迟仍然大约为15毫秒。我认为这很慢。
以下是我的代码的一些片段:
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, port));
server.Listen(1);
// Begin Accept
server.BeginAccept(new AsyncCallback(ClientAccepted), null);
在ClientAccepted中,我设置了NetworkStream,StreamReader和StreamWriter。 这是我在想要更新玩家位置时发送消息的方式:
string message = "P" + "\n" + player.Position + "\n";
byte[] data = Encoding.ASCII.GetBytes(message);
ns.BeginWrite(data, 0, data.Length, new AsyncCallback(EndUpdate), null);
EndUpdate唯一做的就是调用EndWrite。 这就是我收到数据的方式:
message = sr.ReadLine();
它不会阻止我的游戏,因为它是在第二个线程上。
这些是我试过的东西: - 使用IP而不是TCP - 使用二进制消息而不是文本 - 使用IPv6而不是IPv4 什么都没有帮助。
关于如何改善延迟的任何想法?
谢谢
答案 0 :(得分:9)
您看到的延迟很可能是由Nagle's algorithm引起的,用于在使用TCP发送大量小数据包时提高效率。本质上,TCP堆栈将小数据包收集在一起,并在传输之前将它们合并为更大的数据包。这显然意味着延迟一个小间隔 - 最多200ms - 在发送输出缓冲区中等待的内容之前查看是否有更多数据被发送。
因此,请尝试通过在套接字上将NoDelay属性设置为true来关闭Nagle。
但是,请注意,如果对插槽执行大量小写操作,则可能会因TCP标头开销和每个数据包的处理而丢失性能。在这种情况下,如果可能的话,您希望尝试将您的写入一起收集。
答案 1 :(得分:2)
大多数联网游戏不使用TCP而是使用UDP进行通信。问题在于可以轻松删除数据,这是您必须考虑的因素。
使用TCP,主机/服务器之间有许多交互以有序的方式保证数据(您必须考虑使用UDP的另一件事,即未订购数据的事实)。
最重要的是,代码中的计时器必须等到字节从非托管套接字层冒出来,通过非托管代码等等。那里会有一些开销你不会是能够克服(非管理到管理过渡)。
答案 2 :(得分:2)
还有其他几个要考虑的元问题:
定时器精度:许多系统定时器仅每15 ms左右更新一次。我不记得确切的值,但是如果你的时间总是大约15毫秒的倍数,那就要怀疑你的计时器不是很高精度。这可能'不是'你的问题,但要记住它。
如果您在同一台计算机上进行测试,并且只运行一台核心计算机,则线程/进程切换频率将占据您的ping时间。没什么可做但尝试添加Sleep(0)调用以允许在发送数据后进行线程/进程交换。
TCP / IP传输设置。正如之前在SocketOptionName.NoDelay中提到的那样。同样如前所述,如果您不断更新状态,请考虑使用UDP。订单无法保证,但对于易失性数据,丢失的数据包是可以接受的,因为状态很快就会被覆盖。为避免使用陈旧数据包,请向数据包添加递增值,并且永远不要使用标记为当前状态的数据包。 tcp和udp之间的差异不应该占200毫秒,但其他因素的组合可以。
轮询与选择。如果您轮询接收的数据,轮询的频率自然会干扰接收速率。我在专用网络线程中使用Socket.Select来等待传入数据并尽快处理它。
答案 3 :(得分:0)
你说你试过“IP”,你可能通过指定ProtocolType.Ip来做到这一点,但我不知道这究竟是什么意思(即我不知道在引擎盖下选择了什么协议)你将它与SocketType.Stream结合起来。
除了casperOne所说的,如果你使用TCP,那么看看设置SocketOptionName.NoDelay是否会对你产生任何影响。