Various BSD Networking Questions
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
(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
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
(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
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/
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
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
For UDP, it's normal to send a packet sequence number and length at the front of each packet... as binary, not text

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
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 
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
Guess I'm just a bit lost on how this is supposed to be done
-wyrmmage

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

Guess I'm just a bit lost on how this is supposed to be done

-wyrmmage
Worlds at War (Current Project) - http://www.awkward-games.com/forum/
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:
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
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
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().
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 
Thanks for the help everyone, it's much appreciated
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...
-wyrmmage

Thanks for the help everyone, it's much appreciated

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...

-wyrmmage
Worlds at War (Current Project) - http://www.awkward-games.com/forum/
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.
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.
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.
ok, I'll be sure to make use of that advice, Fenris 
Thanks for the help and the speedy replies, everyone
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

Thanks for the help and the speedy replies, everyone

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/
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.
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.
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:
[/edit]
Cheers!
[edit] ahah, just do this:
Code:
struct blahblah
{
float bladeedah;
int bladeedooh;
float blahdeedee;
} __attribute__((packed));Cheers!
It's not magic, it's Ruby.
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.
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.
so that means that this code should be fine:
on all computers, with no packing issues?
Code:
struct blahblah
{
float bladeedah;
unsigned int32_t bladeedooh;
float blahdeedee;
};Worlds at War (Current Project) - http://www.awkward-games.com/forum/
wyrmmage Wrote:so that means that this code should be fine:
on all computers, with no packing issues?Code:
struct blahblah
{
float bladeedah;
unsigned int32_t bladeedooh;
float blahdeedee;
};
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!
Possibly Related Threads...
| Thread: | Author | Replies: | Views: | Last Post | |
| Newbie networking - just dl'ing data from an URL... | sealfin | 4 | 3,235 |
Apr 19, 2007 07:01 AM Last Post: sealfin |
|
| Good networking library | Ummon | 5 | 4,181 |
Jan 22, 2007 11:59 PM Last Post: akb825 |
|
| Simple networking? | CarbonX | 13 | 6,769 |
Apr 7, 2005 12:36 AM Last Post: Andrew |
|
| Saving a Place for Networking | Roosterhouse | 3 | 3,726 |
Aug 6, 2004 02:05 PM Last Post: FCCovett |
|
| Networking for Multiplayer Games Where to start? | NYGhost | 2 | 3,234 |
Jan 28, 2004 09:15 AM Last Post: Skorche |
|

