Jump to content
  • entries
    5
  • comments
    43
  • views
    10,656

Keeping the ne'er do wells out...


Mumbles

2,678 views

 Share

Online games... The unsavoury types are always drawn to them. As a game admin, you simply kick them out of your game, or ban them if you're in such a mood and that's the end of it. But now looking at it from the developer side of things, I can't help thinking a bit more about it.

 

It's one of UDP's advantages over TCP. When you get a known trouble maker trying to join your game, your server can either tell them to sod off (in the nicest way possible), or it could pretend that it never even got the request. Easy.

 

What about when you have TCP, or both? TCP can't just "ignore" a connection request* - it's just not allowed. All you can do is either call accept, which will (normally) block until a connection request actually arrives -- or not, in which case, no one can connect, not even the good guys... It's something most people here won't need to think too hard about, but perhaps a bit if input for those who it will affect (namely: me)

 

 

Most games using a TCP connection system, will continually call the accept function, since it wouldn't be much of a multiplayer game if only the host could play. When a known trouble maker comes along, the server simply closes its side of the connection and refuses to send anymore data. The trouble I learned recently is that you can't actually force the remote peer to close their end of the connection - and you can't delete your socket handle until they do shut the connection, in case they are still sending data to you...

 

Now I know that winsock isn't really built for games. For starter's Nagle's Algorithm starts enabled, and it's highly recommended in the MSDN that you leave it enabled. Yes, it's easy to disable (setsockopt with TCP_NODELAY set to false), but the fact you have to manually disable it at all, says it's not what winsock is really targetted towards.

 

 

So, already I've realised that once an undesirable gets in, I could in theory have them wasting a valid socket resource (if they're not using an official client), by refusing to shut the connection and waiting for the timeout to occur. By recently I found a way round this... Winsock allows something called "conditional accept" which is a callback called by the accept function, where you can look at the sender's IP and port (although the port isn't really helpful at this stage) and decide whether to open the connection, or to refuse it - Thereby causing the socket to get error 10061, as if the server application wasn't running.

 

A little more reading though, and I discover that this conditional accept thing is on the list of "Lamest things supported on win32". Without conditional accept, as soon as a connection request arrives, it's accepted automatically in kernel space, so it's about as fast as it can be, whereas the conditional accept callback is done in user space, and switching between them many times a second takes time. The argument is, that it would be so slow that genuine users can be getting 10060 timed out errors due to the accept not being handled fast enough.

 

Anyone here know anything about kernel stuff? I know nothing about it, but when I asked my brother, his response was "anyone who thinks that switching between kernel space and user space is slow, probably thinks a kernel has something to do with fried chicken". He also suggested that even with several hundred users, a well optimised system would see no slow down in accepting new users via this "slow" callback, and that it would probably only fall over during a SYN-flood (but not using the callback is just as vulnerable).

 

 

Personally, I can't see why other TCP games haven't used conditional accepting. It's been around for years, so professional companies must think there's something wrong with it. But what? It seems like such an obvious solution. Why do we need to say: "no, you're not allowed in... Go away please" when we could simply pretend there's no one home (Same as they can do on UDP)?

 

 

*Actually TCP can ignore connection requests. During the conditional accept callback simply don't return a known value (CF_ACCEPT or CF_REJECT) and your socket will get error 10061 (rebind the socket and then you can carry on using it) but their socket will get 10060, as if the server wasn't even switched on... However, it's a cheap hack and probably not a good idea to use it, since this is undocumented behaviour.

  • Upvote 1
 Share

4 Comments


Recommended Comments

It's truly astounding the number of details you have to take into account as you develop a networked multi-player game. Good info, but most everything I have read suggests using a modified UDP for games. What sort of advantages does using TCP bring?

Link to comment

The main advantage TCP gives is "guaranteed delivery". Packets you send via TCP will be processed in the order they were sent (packet reordering) and will be retransmitted if lost. Also, having a TCP stream associated with a client allows you to do authentication when the stream is established and not worry about it after that. It also makes spoofing the source IP much, much harder due to TCP's three way handshake.

 

Everything TCP does can be implemented on top of UDP application side, but eventually you end up just remaking TCP, but with all the additional stuff having to be handled in your application instead of by the networking stack.

 

Many game libraries use UDP and selectively re-implement those features of TCP that they need, such as discarding late packets or requesting retransmittal of lost packets.

 

It comes down to using the right tool for the job. Often a mixture of the two is the correct approach.

Link to comment

What if your using a remote client call, and have your server tell the client to close its connection? I realize you would be in effect "accepting" the connection first, but then immediately closing it with your client check function.

Link to comment

What sort of advantages does using TCP bring?

 

Where speed isn't so crucial (RTS games for example) it does everything that reliable, ordered UDP does, but it's not handled by your application, it's basically handled by the OS. It's almost sure to be better optimised if it's part of the OS rather than having to do bits yourself.

 

Currently I'm looking towards using both UDP and TCP side by side, both performing different tasks, but working in harmony:

 

UDP for the vast majority of the data:

Clients telling the server how they wish to move, or interact with the world

The server keeping all clients informed about updates to the world, and other players. (These are only delta updates, up to 40 per second default)

 

TCP for everything else:

The server sending definite (timestamped) states of the world for clients to base their received updates from (no faster twice a second)

Players chatting

Downloading extra packages from the server (File transfer is one case where there is no point reinventing the TCP wheel on UDP).

Latency measuring (Yes, it could go on the UDP socket, but I'm keeping that socket as clear as I can, it already has a very high frequency of packets on it)

 

As for when would anything be ready to show? Months... at least.

 

 

I realize you would be in effect "accepting" the connection first, but then immediately closing it with your client check function.

 

I've since found out that actually, winsock's not as bad as I first thought. You don't have to wait for the peer to shutdown their socket before you can delete it. Using setsockopt you can change a value called SO_DONTLINGER to true (or 1 because it's C not C++). Now you can shutdown your socket as normal, but if you then proceed to call closesocket() before the remote peer shuts down, rather than crashing your application, it gives the remote peer error 10054 "connection reset by peer" on their next call to send() recv() or shutdown()

 

It's just that SO_DONTLINGER is set to false (0) by default, but once it's changed you can pull the plug on any player you don't like without any warning, and without having to wait for them to shutdown too.

 

There's also probably less to go wrong with this method than conditional accept, so I may end up not using that. Yes, it confirms to a different sort of trouble maker that your application is actually running, but I think that's probably too insignificant to spend too much time worrying about.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...