Various BSD Networking Questions

Moderator
Posts: 371
Joined: 2006.08
Post: #1
I got my server program up and running, and my client as well, some time ago. I then started to work on the actual development of the game, but the FPS was far too slow, so I re-wrote my graphics engine; this enabled me to take advantage of some features that are normally written into networked games. Now that the graphics engine is done, I've decided to re-write all of my old server and client program's code, and I'm looking for some easier ways to do things this time. I'm using WinSock on Windows, and the equivalent on Mac (just regular BSD sockets, I think?), because I can get the same code working on both platforms with just a few IFNDEF statements Smile (note that I'm using TCP/IP, not datagrams, at the moment)
So, here are the questions:

Is it possible to know how long the entire message is going to be just by the first packet that is sent? A friend of mine told me that this was possible, but I couldn't really find any code that worked Annoyed I was just using a five-digit char* stream which I then parsed back into a number when the message was received. While this works, it's extremely ineffecient, so I'm looking for another, better way to do this.

What would be the best way to determine when a client has a lag spike? I know that, since I'm using TCP/IP, packets are re-sent when one doesn't make it to the client in a certain amount of time...is there any way of being notified of this, or would I just be better off having my server send a full update every once in awhile to make sure that everything was in the right spot/right frame of animation?

Thanks ^_^
-wyrmmage

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
For real-time networking, TCP has highly undesirable behaviors, which you can't work around. I'd suggest you move to UDP.

For UDP, it's normal to send a packet sequence number and length at the front of each packet... as binary, not text Rasp

You will know when a client is having connection issues (but not what kind of issues) when the last-acknowledged packet is a long time in the past.

http://trac.bookofhook.com/bookofhook/tr...Networking
Quote this message in a reply
Moderator
Posts: 371
Joined: 2006.08
Post: #3
hmmm...it kind of makes sense to move to UDP, I suppose, although I'm curious as to what undesirable effects TCP has on real-time games? I'm still in the process of reading through the links from the sticky, but thanks for the link Smile
When you say 'as binary, not text', I'm not exactly sure what you mean....I thought everything you send() had to be in a char array? Or is there another function (or variation of send()) that lets you send binary streams? (and what exactly is meant by a binary stream?) I mean, you can't control individual bits very easily in C or C++ (at least not that I'm aware, although that doesn't mean much XD) unless you just mean using an array of boolean values? If so, I'm not sure how you would go about using send() to send them to the client/server unless you could just pass the boolean array to the send() function.
Thanks for the help so far Smile
Guess I'm just a bit lost on how this is supposed to be done Wink
-wyrmmage

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
TCP ensures that everything gets through, which means that a player whose connection can't handle the bandwidth can't participate at all, and a player whose bandwidth is barely sufficient but experiences momentary lag may never catch up. Also, TCP doesn't always send at the maximum possible speed, and may introduce latency either at the sender or at the receiver as things get buffered.

What you pass to send is any old pointer to any old piece of data. There is no restriction on it being textual.

If you want to be really naive, you can just do something like:

Code:
#include <stdint.h>

typedef struct Message
{
    uint32_t packet_number;
    uint32_t packet_length; // not necessary here...
    struct
    {
        float x;
        float y;
        float velocity_x;
        float velocity_y;
        uint32_t powerups;
    }
    players[MAX_PLAYERS];
}
Message;

....

Message m;
// fill in m
send(......, &m, ......);

Note that since you're then sending binary data, and not dealing with endianness, you won't be able to network between PowerPC and Intel machines.

Dealing with endianness is simple, but probably a topic for another thread Wink
Quote this message in a reply
fakeOne
Unregistered
 
Post: #5
OneSadCookie Wrote:What you pass to send is any old pointer to any old piece of data. There is no restriction on it being textual.

WinSock only accepts const char * for the message argument for send().
Quote this message in a reply
Moderator
Posts: 371
Joined: 2006.08
Post: #6
meh, that's kind of what I thought...is there any way to use BSD sockets on Windows without using Winsock (and preferable not a thrdr-party library that extends the functionality of BSD sockets)? I know, theoretically, how to deal with endiannes, although I've never used it on the messages that I've been sending Smile
Thanks for the help everyone, it's much appreciated Smile
Edit: if I just cast my data structure as a const char*,and then re-cast it on the receiving end, will the information end up as a usable data structure? I'm guessing not, but I dunno... Annoyed
-wyrmmage

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #7
fakeOne Wrote:WinSock only accepts const char * for the message argument for send().

So?

Doesn't change the functionality.

wyrmmage Wrote:Edit: if I just cast my data structure as a const char*,and then re-cast it on the receiving end, will the information end up as a usable data structure? I'm guessing not, but I dunno...

Yes, that should be fine.
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #8
Just to give some quick pointers about endian-ness: do it, and do it now. It's not the sort of thing you want to bolt on when it's all running, because you have to find every value that isn't a char that you're sending and fiddle with it.

The fiddling, on the other hand, is simple. Include <arpa/inet.h> and use htonl (host to network long), ntohl (network to host long), htons (host to network short) and ntohs (network to host short). On the sending end, you should convert from host to network, and on the receiving end from network to host.
Quote this message in a reply
Moderator
Posts: 371
Joined: 2006.08
Post: #9
ok, I'll be sure to make use of that advice, Fenris Wink
Thanks for the help and the speedy replies, everyone Grin
My only concern at the moment stems from reading this page: http://tangentsoft.net/wskfaq/articles/e...e-tcp.html
where it says:
To illustrate the structure padding problem, consider this C declaration:

struct foo {
char a;
int b;
char c;
} foo_instance;

Assuming 32-bit ints, you might guess that the structure occupies 6 bytes, but this is not so. For efficiency reasons, compilers "pad" structures to align the data members in a way that is convenient for the CPU. Most CPUs can access 32-bit integers faster if they are at addresses evenly divisible by 4, so the above structure would probably take up 12 bytes on these systems. This issue rears its head when you try to send a structure over Winsock whole, like this:

send(sd, (char*)&foo_instance, sizeof(foo), 0);

Unless the receiving program was compiled on the same machine architecture with the same compiler and the same compiler options, you have no guarantee that the other machine will receive the data correctly.

The solution is to always send structures "packed" by sending the data members one at a time. You can force your compiler to pack the structures for you, with a resulting speed penalty in the code that accesses those structures. Visual C++ can do this with the /Zp command line option or the #pragma pack directive, and Borland C++ can do this with the -a command line option. Keep the byte ordering problem in mind, however: if you send a packed structure in place, be sure to reorder its bytes properly before you send it.


I guess this information is wrong, or I don't have to worry about it?
Thanks again everyone ^_^
-wyrmmage

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #10
The safe way to do that is to convert everything to a data type of a known size before writing it to your array of binary data. One easy way to do this is to include <stdint.h> and use the types it defines (int32_t, etc.), which are guaranteed to be the size specified in their name. As long as everything is read and written as a guaranteed-size data type and you do the appropriate endianness conversion on both ends, you should be safe.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #11
The struct-packing thing can be an issue in theory, but in practice it's not as bad as it sounds. If you send only float, (u)int32_t, (u)int16_t and (u)int8_t, the packing will be the same on Mac/PowerPC, Mac/Intel, Windows/Intel and Linux/Intel.
Quote this message in a reply
Member
Posts: 567
Joined: 2004.07
Post: #12
or, you could simply disable the packing (a) at the gcc level, or (b) the struct level with a special (pragma?) instruction.

[edit] ahah, just do this:

Code:
struct blahblah
{
float bladeedah;
int bladeedooh;
float blahdeedee;
} __attribute__((packed));
[/edit]

Cheers!

It's not magic, it's Ruby.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #13
you still shouldn't have non-fixed-width types (like int) in your struct declaration.

Also, packed structs can be massively less efficient to deal with, so you should get the information out of them as quickly as possible into regularly aligned structs.
Quote this message in a reply
Moderator
Posts: 371
Joined: 2006.08
Post: #14
so that means that this code should be fine:
Code:
struct blahblah
{
float bladeedah;
unsigned int32_t bladeedooh;
float blahdeedee;
};
on all computers, with no packing issues?

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #15
wyrmmage Wrote:so that means that this code should be fine:
Code:
struct blahblah
{
float bladeedah;
unsigned int32_t bladeedooh;
float blahdeedee;
};
on all computers, with no packing issues?

that code is fine.. its just a struct definition.
What is not fine is to memcpy it into a buffer output that buffer, the result may be platform specific.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Newbie networking - just dl'ing data from an URL... sealfin 4 3,510 Apr 19, 2007 07:01 AM
Last Post: sealfin
  Good networking library Ummon 5 4,477 Jan 22, 2007 11:59 PM
Last Post: akb825
  Simple networking? CarbonX 13 7,166 Apr 7, 2005 12:36 AM
Last Post: Andrew
  Saving a Place for Networking Roosterhouse 3 3,995 Aug 6, 2004 02:05 PM
Last Post: FCCovett
  Networking for Multiplayer Games Where to start? NYGhost 2 3,453 Jan 28, 2004 09:15 AM
Last Post: Skorche