Advertisement

send() and recv() not sending whole buffer

Started by February 20, 2003 08:22 PM
46 comments, last by AntiVeggieBoy 21 years, 11 months ago
Just use a header. My TCP Winsock class works flawless. I have as many as 4 messages come through and maybe a partial 5th in a single recv and it doesn''t flinch. It knows exactly where each message begins and ends and breaks it up accordingly. Then leaves the partial message in the buffer until the rest of it shows up.

This isn''t a TCP issue. It''s an issue of not realizing you can''t send raw data without some way of telling the recieving end what is it.

A header at a minimum needs to store the message length and the message type. I use an INT but a SHORT should be fine unless you plan on sending more than 64KB per message which you shouldn''t be.

Ben


IcarusIndie.com [ The Labyrinth | DevZone | Indie Mail | Hosting | Tiberian Merchandise | GameShot | Fun With Cutouts ]
quote:
Original post by KalvinB
This isn't a TCP issue. It's an issue of not realizing you can't send raw data without some way of telling the recieving end what is it.


It is, in its entirety, a TCP issue. TCP is streamed, and as such, has no concept of message boundaries. It can only be assumed that any network application will have some kind of protocol defined, whether it uses a 'header' is not the issue (e.g. you can use text streams without a 'header' - MUDs do this). In fact, this is one of the most common problems people have on this forum. They don't understand the difference between packets and streams (or UDP/TCP). Maybe we should have our moderator add this one to the FAQ?

quote:
Original post by daveb
A word to the wise : using TCP will only bring you heartache in the long run. In a "serious" application, the giant wall you will inevitably hit is that (wait for it...)

I'm a UDP fan all the way. The game companies I've worked for or dealt with are also UDP fans. However, you have to know when the benefits outweigh the cons of UDP. "'serious' application"s include things like the front end patcher of a MMO, which probably should use bulk transfers of TCP. Of course, HTTP is pretty serious too. Even real-time media protocols like RTP can use TCP. Basically Daveb, I agree with you, mostly. But I've gotten away from the "UDP-or-die" mentality (at least for posting purposes And yes, I implement my own reliability on UDP

quote:
Original post by zppz
quoted "Also - sending structs is a no-no. Break them down into their component parts and package them individually into a packet."

Why?


I believe he means to simply 'pack' your structures.
struct my_struct
{
int num;
char this_is_bad_coding;
char *string;
}theStruct;

Two issues here.
1. you aren't going to send a pointer across the network, so you have to pack the _data_ into another buffer (and probably a size value with it) and send that instead of the 4-byte pointer value.

2. this_is_bad_coding for is named such a reason, and it has nothing to do with naming conventions. Pad bytes. Structures follow an alignment scheme such that members are aligned on some byte boundary depending on the largest default alignment of it's member types. So if you have a struct{short, int, short}, the sizeof(struct) is NOT 8. You can't create a pointer to struct and increment sizeof(short) to access the int. It doesn't work, and You get a bus error on most systems, maybe a segfault on others. By packing your structures you are going to save on some late night debugging sessions. Yes, you CAN send structs across the network. But in most complex applications you are going to want to pack them.

[edited by - fingh on February 21, 2003 1:39:03 PM]
Advertisement
Bingo. By "serious" application, I mean a game setting where you will have a potentially overhwhelming amount of data that needs to get efficiently sent. If you're dealing with an analog modem and bandwidth requirements on the FPS/realtime level, TCP is a terrible terrible choice. Its just too easy for your thing UDP layer to get flooded, or have the internal socket buffers get packed to the brim - causing unavoidable timeouts on the reliable sockets. Your TCP stack decides it wants to disconnect? Too bad!

For stuff like HTTP and patching tools, sure - TCP is great. IE uses it after all. But when your network device is taking a pounding and being made to push its bandwidth limits (not to mention your CPU), its all to easy to have TCP give up on you with no warning.

Again, its not terribly hard to write your own reliable layer using UDP. And in most game settings, 99% of your packets are sent unreliably anyway, so there's no need for the heavyweight and unstable TCP connection there (plus it -way- simplifies the low level connection code. There's no listen() and connect() calls or any of that baloney). Once your socket is initialized, its sendto() and recvfrom() and that's it. It makes the intrusion of the actual network layer extremely minimal in your code.


Edit : in addition to structure packing issues, there's endian ordering issues. If you have macros like PACK_INT() and UNPACK_INT() its a triviality to make the network code work between different endian machines. Whereas if you're packing structs, you'll end up grovelling through hundreds/thousands of lines of code trying to find all the places where you're obliterating endian-ness.

[edited by - daveb on February 21, 2003 4:00:45 PM]

[edited by - daveb on February 21, 2003 4:06:36 PM]
Volition, Inc.
So here it is...

I have an SRemote class that represents a remote connection
it holds a TCP socket - don''t worry it will have a UDP one for unreliable sends soon

then I have an SServer that holds a vector of SRemotes that represents each client that
connects. These are polled for data (I like polling, deal with it :> ).

the SClient class holds one SRemote representing the server it is connected to.

an SRemote can :

Send(char* pMessageBuffer, UINT messageSize, char messageType)
this packs the messageSize, type and the message into a buffer and
and send()''s it


and can see if :

bool IsMessageWaiting();

and can :

GetLastMessage(char* pMessageBuffer, UINT messageSize, char messageType)

in Send()

should I loop on winsock send() until the entire pMessageBuffer is sent
or will this cause delays?

in IsMessageWaiting()

I have a static temp holding buffer that gets the message using recv().
it reads the size and type and thus know what to expect from now on.
I suppose I should get as much of the message as possible and save it in the
temp buffer and then return false (no message waiting)
and only return true when the entire message is read and then allow the SClient or
SServer to ::GetLastMessage()

Seems like a good design to me...
Uhhh...
Also - every 100ms, the client sends his/her playerState message
with positional/orientation info (only if changed of course)
and the server sends to all clients a gameState message
this gameState holds :

int numPlayersInGameState (how many players are references in this tick

then FOR EACH player

int flags (what changed in last tick - heading, pitch, pos,
fired etc. held in bitmasks

and then, based on flags the new heading, pitch and pos are included
the heading and pitch are sent in a byte unless fired is true
in which case we need a more accurate vector and floats are used.
pos is still in floats (any help on compressing these would be cool)

now, with 8 clients, sending the gameState buffer every 100ms the server sends out
about 1K of data every second. (obviously changing depending
on what the clients are up to.)
Is this workable? How big is the average Quake3 gameState?


Uhhh...
quote:
Original post by fingh
2. this_is_bad_coding for is named such a reason, and it has nothing to do with naming conventions. Pad bytes. Structures follow an alignment scheme such that members are aligned on some byte boundary depending on the largest default alignment of it''s member types. So if you have a struct{short, int, short}, the sizeof(struct) is NOT 8.


#pragma pack(1) takes care of the alignment.

-------------Ban KalvinB !
Advertisement
I take it the lack of replies means you''re all out copying my great design...
Uhhh...
quote:
Original post by AntiVeggieBoy I have a static temp holding buffer that gets the message using recv(). it reads the size and type and thus know what to expect from now on. I suppose I should get as much of the message as possible and save it in the temp buffer and then return false (no message waiting)
and only return true when the entire message is read and then allow the SClient or SServer to ::GetLastMessage()

Seems like a good design to me...

and to me, this is how I made my socket wrapper
One thing though, I hope you don''t mean ''static'' buffer as in a ''static'' class variable - when you have more than SRemote instance they would write into the same holding buffer!

----------
Dauntless for President!
zppz.homeip.net <- game dev stuff
Implemented last night.
Works like a charm - now to test at work...
I recommend all network game programmers to get a job working graveyard shifts at an internet cafe ;~
It feels good when so many years of learning begin to come together to an actual product.
I encourage everyone to try Winsock - this stuff''s cooler than graphics programming...




Uhhh...
"TCP is streamed, and as such, has no concept of message boundaries"

"They don''t understand the difference between packets and streams"

It''s irrelavent. You make a big deal out of nothing over UDP everytime you post here. Whether you use TCP or UDP you should have a header at the minimum telling the recieving end how big it is and what it is. You can''t just throw a message at something and pretend it''s going to know what it is and how much data is has.

If you actually know what you''re doing and just use a header it doesn''t matter if you use UDP or TCP in terms of getting the messages out of packets.

"you can use text streams without a ''header'' - MUDs do this"

That''s because MUDs only have one message type genius.

If the message can only be one thing then you really don''t need to tell the reciveing end what it is now do you? And you don''t need to know how much data there is because it''s all being put on the screen anyway.

Server doesn''t have to worry about broken packets with TCP because . commands are contained in a small enough number of bytes that they won''t be broken up. Maybe once in a million packets something will go wrong and .kill monster will turn into .kill mon but it''s irrelavent because either the MUD will ignore the packet (monster mon doesn''t exist) and you just have to send the command again or it tries to assume what you meant with a text completion routine.

"I''m a UDP fan all the way"

More like a blind facination. You pretend there are problems where there are none and then claim UDP is better.

You DON''T need to understand the difference between a packet and a stream to use TCP or UDP although you''d like to insist otherwise. It''s trivial. If you have a header (as you should) it''s useless information.

Ben


IcarusIndie.com [ The Labyrinth | DevZone | Indie Mail | Hosting | Tiberian Merchandise | GameShot | Fun With Cutouts ]

This topic is closed to new replies.

Advertisement