views:

1507

answers:

4

Hi, I am working on a little online multiplayer pong game with C# and XNA.

I use sockets to transfer data between two computers on my personnal LAN. It works fine.

The issue is with speed : the transfer is slow.

When I ping the second computer, it shows a latency of 2 ms. I set up a little timer inside my code, and it shows a latency of about 200 ms. Even when the server and the client are on the same computer (using 127.0.0.1), the latency still about 15 ms. I consider this as slow.

Here are some fragment of my code :

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);

in ClientAccepted, I set up a NetworkStream, a StreamReader and a StreamWriter. This is how I send a message when I want to update the player's location :

string message = "P" + "\n" + player.Position + "\n";
byte[] data = Encoding.ASCII.GetBytes(message);
ns.BeginWrite(data, 0, data.Length, new AsyncCallback(EndUpdate), null);

the only thing EndUpdate do is to call EndWrite. This is how I receive data :

message = sr.ReadLine();

It dosen't block my game since it's on a second thread.

These are the stuff I tried : - Use IP instead of TCP - Use binary message instead of text - Use IPv6 instead of IPv4 Nothing really did help.

Any thoughts about how I can improve the latency?

Thank you

+3  A: 

Most networked games don't use TCP but UDP for communications. The problem with that is that data can be easily dropped, which is something you have to account for.

With TCP, there are a number of interactions between the host/server to guarantee data in an ordered manner (another thing you have to account for using UDP, the fact that data is not ordered).

On top of that, the timer in your code has to wait until the bytes bubble up from the unmanaged socket layer through unmanaged code, etc, etc. There is going to be some overhead there that you aren't going to be able to overcome (the unmanaged to managed transition).

casperOne
Also, ping uses UDP rather than TCP.
Andrew Kennan
It's true that UDP trades reliability for efficiency, but TCP protocol overhead doesn't explain a 15ms latency, much less a 200ms one.
Mike Scott
A: 

You said you tried "IP", and you presumably did that by specifying ProtocolType.Ip, but I don't know what that really means (i.e. I don't know what protocol is chosen 'under the hood') when you combine that with SocketType.Stream.

As well as what casperOne said, if you're using TCP then see whether setting SocketOptionName.NoDelay makes any difference to you.

ChrisW
+6  A: 

The latency you're seeing is most likely due to Nagle's algorithm which is used to improve efficiency when sending lots of small packets using TCP. Essentially, the TCP stack collects small packets together and coalesces them into a larger packet before transmitting. This obviously means delaying for a small interval - up to 200ms - to see if any more data is sent, before sending what's waiting in the output buffers.

So try switching off Nagle by setting the NoDelay property to true on the socket.

However, be aware that if you do a LOT of small writes to the socket, that you may lose performance because of the TCP header overhead and processing of each packet. In that case, you'd want to try to collect your writes together into batches if possible.

Mike Scott
+1  A: 

There are several other meta-issues to consider:

  1. Timer accuracy: Many system timers only update every 15 ms or so. I can't remember the precise value, but if your timings are always multiples of about 15 ms, be suspicious that your timer is not high precision. This is 'probably' not your issue, but keep it in mind.

  2. If you're testing on the same computer, and only running a single core machine, the thread/process switching frequency will dominate your ping times. Not much to do but try to add Sleep(0) calls to allow thread/process swaps after sending data.

  3. TCP/IP transmission settings. As alluded earlier in SocketOptionName.NoDelay. Also as mentioned earlier, consider UDP if you're continuously updating state. Order isn't guaranteed, but for volatile data missing packets are acceptible, as the state will be overridden soon anyway. To avoid using stale packets, add an incrementing value to the packet and never use a packet labelled earlier than the current state. The difference between tcp and udp should not account for 200 ms, but a combination of other factors can.

  4. Polling vs. Select. If you're polling for received data, the frequency of polling will interfere with receive rate, naturally. I use Socket.Select in a dedicated network thread to wait for incoming data and handle it as soon as possible.

Nick Gebbie