A couple basic UDP questions...

Started by
7 comments, last by Dookie 17 years, 9 months ago
Hey guys, it's me again! Thanks to this forum and hplus0603's help, I think I'm actually making some headway on the multiplayer ability of my game. You guys ROCK! I have a couple of basic questions, since I'm still pretty new to all this 'send/rcv' stuff. 1) What size should I limit a message to, to ensure that the message goes to the recipient without getting broken up? I presume that if the message is too big, then the network will break it up into more than one piece and then I'm left with dealing with how to reassemble the message on the other size. And with UDP, it's not guaranteed that I'll receive the pieces in the right order (which adds another issue to the problem). So I'd like to know of what size I should keep my messages, so they don't get broken up and transmitted across several UDP packets. 2) See the below... int recvfrom( SOCKET s, char* buf, int len, int flags, struct sockaddr* from, int* fromlen ); That's Microsoft's definition of the 'recvfrom()' function. I understand SOCKET s, char* buf, int len, int flags, and struct sockaddr* from. But what is 'int* fromlen'? Is it a value I give to 'recvfrom()', or is this a value received after 'recvfrom()' is successfully called? If it's a received value, then is it the total size of a message if it's broken up into multiple pieces, or is this only the size of this packet? If it's a sent value, then what value should it be? I think that's all the questions I have for now... I want to make sure I code this thing right so I don't have to do a bunch of cleanup work after discovering I'm doing everything wrong! Thanks in advance for the help!
"The crows seemed to be calling his name, thought Caw"
Advertisement
seems like last parameter is optional and is size of second to last parameter holding socket address of sending socket

Kuphryn
Quote:Original post by kuphryn
seems like last parameter is optional and is size of second to last parameter holding socket address of sending socket

Kuphryn


Correct.

sizeof(SOCKADDR) should work.
Hey dokie !
The message size depends on the server client , try and read about "lower water mark" and "upper water mark " in UDP .
If you cannot find them than I will explain it here ....
Thanks for the info, guys! I'll go ahead and use the size of 'sockaddr' for that last parameter.

I did a little research on what you mentioned, atultuff. Doesn't look like the Windows version of winsock supports those options... But by using 'setsockopt()', I can set the size of a 'receive' buffer (SO_RCVBUF) and 'send' buffer (SO_SNDBUF). Are either of those the same thing, or are those simply buffers on the computer itself rather than how large a packet can be?
"The crows seemed to be calling his name, thought Caw"
If you are using UDP, then, even if the underlying network fragments the packet, the packet will be re-assembled before it's delivered to you. If it can't be re-assembled, it's dropped. Thus, you don't have to worry about fragmentation from that point of view.

If you're using TCP, then even a very small packet may get split or, more likely, combined with another packet, depending on the implementation and network conditions. Thus, you can never expect a single send() to relate to a single recv() in TCP.

For packet size, the first thing you should worry about is network usage -- you want packets to be small, such that they don't take "forver" to send on your network, and if you're multi-player, all packets to all players need to fit through your uplink (which may be as low as 64 kbps on some cable modems, and is often no more than 256 kbps on DSL). The "b" stands for "bits" btw :-)

Now, that being said, I try to limit UDP packets to < 512 bytes. I know that that will fit well within the MTU used on certain modems (typically 560), and it will also fit well within the Ethernet fragment size (1536 bytes) -- even when the overhead of UDP and IP is added. At the same time, 512 bytes over a 64 kbps link is 1/16th of a second, or ~63 milliseconds -- if you cram the line full with 2 updates per second for 8 players, the time between the first and the last packet being sent for the same tick will be almost half a second!

Another convenient size is 255 bytes, because you can use a single byte for various "packet size" header fields :-)
enum Bool { True, False, FileNotFound };
Awesome! Thanks for the info hplus0603, that clarifies a lot of other questions I had.

I'm still having troubles sending different types of structs across the network, though... I have four structs: client_connect, client_chat, client_gameDat, and server_gameDat. Every once in a while, recvfrom() on the server will return an erroneous size that sometimes corresponds with the size of a struct and the data will go corrupt when memcpy'd to that struct (which makes game objects' positions go haywire). Rather than use four separate structs, is there a better way to do this? Should I use a socket for sending client_connect and chat_data, and another socket for sending game_data? Or is there a way to consolidate all the information into one struct?
"The crows seemed to be calling his name, thought Caw"
Don't just rely on the size of the packet... use some kind of header information (like a single byte)...

So that you can say:
[header chat] [chat struct]

For a packet.

This also gives you the oppurtunity to (within a single packet) say:
[header chat] [chat struct] [header player] [player 1 data] [header ...

which eliminates much of the UDP overhead (about 30 bytes per packet)

Just make sure you've got some nice header file with:
static const unsigned char HEADER_CHAT = 0;
static const unsigned char HEADER_PLAYER = 1;
// etc

Also, for the maximum size of a packet... google PMTU or Path Maximum Transfer Unit. It's possible to discover this for any particular link, but as a rule of thumb, values between 200 and 500 bytes are "safe enough" for most things.

As has been said earlier, if you do get fragmented, you'll still see the right data unfragmented from recvfrom, but the thing to keep in mind is it tends to introduce additional lag and CPU overhead. It's always better to not let your packets fragment :)
Awesome, thanks again!

I'd never used a union before, so I had to research it... A struct, except it uses the same memory pool for all of the elements that make up this struct? Pretty friggin' cool stuff! So I tried the idea of using a struct with header data (an int) and a union (consisting of three structs - gameData, clientData, and chatData). It works great, and there's no strange anomalies that happen when the wrong sized datasize is returned from recvfrom().

Thanks again for all the help, I really appreciate it! [smile]
"The crows seemed to be calling his name, thought Caw"

This topic is closed to new replies.

Advertisement