Sending and receiving structs (I read FAQ)

Started by
11 comments, last by dpadam450 12 years, 11 months ago


void send(){
PlayerPosPacket packet;
packet.x = x;
packet.y = y;
packet.player = id;
//char szBuffer[1024];
//SendData((char const *)&packet);
send(mySocket,(char const*)&packet,sizeof(packet),0);
//send(mySocket, buffer, strlen( buffer ), 0 );
ZeroMemory(&packet, sizeof(packet));
}

void recv(){
PlayerPosPacket recvPacket;
int r = recv( mySocket, (char *)&recvPacket, sizeof(recvPacket ), 0);
textprintf_ex(screen,font,0,0, makecol(0,255,0), -1, "x %d", recvPacket.x);
if(r > 0){
x2 = recvPacket.x;
y2 = recvPacket.y;

textprintf_ex(screen,font,0,0, makecol(0,255,0), -1, "x %d", recvPacket.x);
//ZeroMemory(&recvPacket, sizeof(recvPacket));
//allegro_message("here");
}else{}
}


The above code attempts to send the struct to another multi-client server I wrote. The server takes the data from one person and sends it to every other person except the one who send it in the first place. The problem is that I never receive the data I need to receive. I always receive either 0, or some other random number like 675390. Is there something wrong with my char* to struct. Please help me find the problem and fix it. It may be that I am missing some fundamental c++ skill that I somehow overlooked. (Although it is unlikely)

*edit*
What would be nice is an example of a client and a server sending a struct and then viewing the information.

forgot to mention, this is nonblocking.
Quote:Original post by rnw159Screw game maker.There's your sig quote for the forums :D[/quote]
Advertisement
Hmm looking at your code there are a few fundemental things wrong with your implementation.. Assuming this is all win32 sockets:

Firstly the call to "recv" does not guarantee to read the entire packet as you're expecting it to. You need to encapsulate it within a loop that reads from the incoming stream until the expected amount of bytes are read as indicated by the return value of the function call.

Same applies to the call to send - it doesn't guarantee that the whole packet is sent within 1 call.

Next simply reading and writing to the packet structure as a buffer is a very bad way of doing things for a number of reasons - but foremost it's highly likely byte ordering on the recipient end will probably not be as you expect it to. If you're doing things with floating point values then you're really asking for trouble (look up nthol and htonl and others). Attempt to write a manual serialization for each data type within the packet. Also be aware that byte packing may be different the client/server so the sizeof(packet) may return different values.

There are probably a few more issues but I hope that sets you in the right direction.
- Teach a programmer an answer, he can code for a day. Show a programmer the documentation, he can code for a lifetime.

Hmm looking at your code there are a few fundemental things wrong with your implementation.. Assuming this is all win32 sockets:

Firstly the call to "recv" does not guarantee to read the entire packet as you're expecting it to. You need to encapsulate it within a loop that reads from the incoming stream until the expected amount of bytes are read as indicated by the return value of the function call.

Same applies to the call to send - it doesn't guarantee that the whole packet is sent within 1 call.

Next simply reading and writing to the packet structure as a buffer is a very bad way of doing things for a number of reasons - but foremost it's highly likely byte ordering on the recipient end will probably not be as you expect it to. If you're doing things with floating point values then you're really asking for trouble (look up nthol and htonl and others). Attempt to write a manual serialization for each data type within the packet. Also be aware that byte packing may be different the client/server so the sizeof(packet) may return different values.

There are probably a few more issues but I hope that sets you in the right direction.


Thanks thats great! Do you know any examples of this implementation? CAn I see some source code?
Quote:Original post by rnw159Screw game maker.There's your sig quote for the forums :D[/quote]
I haven't played around with raw win32 sockets for a while as I'm quite fond of the abstraction that boost::asio provides. I do think however it is very important to understand the key underlying concepts as you are doing by working at the lower level so I'll give you some examples. This is from the top of my head without much thinking about it so usual disclaimers apply....

This is an example of sending data... receiving is similar in principle. This probably isn't your problem because sending just 8 bytes is likely to succeed in one call but this is necessary nontheless.

// Encapsulating sending a buffer of generic data. Accepts the array of bytes to send
// and the number of bytes to send. (True success, all sent, false failure). This is blocking.
bool sendData(char *i_buf, int i_bufLen) {
int offset = 0;
int rc;
while (offset < i_bufLen) {
rc = send(socket, i_buf+offset, i_bufLen-offset);
if (rc == SOCKET_ERROR) return false;
offset += rc;
}
return true;
}


Your main concern is converting your data types into a common format supported at your client and server ends. Here is an example of receiving and translating data:


char recvData[256];

uint32_t playerX = 0;
uint32_t playerY = 0;

if (receiveData(recvData, 256)) { // Similar to sendData
// We've succesfully read 256 bytes of data, we know that the stream starts with 2 4-byte integers. Read them:

// Read each byte into the integer representing the player position (similar to the way I'd do it)
playerX = recvData[0] << 24;
playerX |= revData[1] << 16;
playerX |= revData[2] << 8;
playerX |= revData[3];

playerY = recvData[4] << 24;
playerY |= revData[5] << 16;
playerY |= revData[6] << 8;
playerY |= revData[7];

// Or use the helper function for this something like this:
// playerX = ntohl(*(uint32_t*)recvData);
}


There's a whole lot more I'd recommend doing - think about implementing your own message buffer class that serializes / deserializes different data types into an appropriately encoded byte array.I hope this gives you an idea of what you need to do.
- Teach a programmer an answer, he can code for a day. Show a programmer the documentation, he can code for a lifetime.

I haven't played around with raw win32 sockets for a while as I'm quite fond of the abstraction that boost::asio provides. I do think however it is very important to understand the key underlying concepts as you are doing by working at the lower level so I'll give you some examples. This is from the top of my head without much thinking about it so usual disclaimers apply....

This is an example of sending data... receiving is similar in principle. This probably isn't your problem because sending just 8 bytes is likely to succeed in one call but this is necessary nontheless.

// Encapsulating sending a buffer of generic data. Accepts the array of bytes to send
// and the number of bytes to send. (True success, all sent, false failure). This is blocking.
bool sendData(char *i_buf, int i_bufLen) {
int offset = 0;
int rc;
while (offset < i_bufLen) {
rc = send(socket, i_buf+offset, i_bufLen-offset);
if (rc == SOCKET_ERROR) return false;
offset += rc;
}
return true;
}


Your main concern is converting your data types into a common format supported at your client and server ends. Here is an example of receiving and translating data:


char recvData[256];

uint32_t playerX = 0;
uint32_t playerY = 0;

if (receiveData(recvData, 256)) { // Similar to sendData
// We've succesfully read 256 bytes of data, we know that the stream starts with 2 4-byte integers. Read them:

// Read each byte into the integer representing the player position (similar to the way I'd do it)
playerX = recvData[0] << 24;
playerX |= revData[1] << 16;
playerX |= revData[2] << 8;
playerX |= revData[3];

playerY = recvData[4] << 24;
playerY |= revData[5] << 16;
playerY |= revData[6] << 8;
playerY |= revData[7];

// Or use the helper function for this something like this:
// playerX = ntohl(*(uint32_t*)recvData);
}


There's a whole lot more I'd recommend doing - think about implementing your own message buffer class that serializes / deserializes different data types into an appropriately encoded byte array.I hope this gives you an idea of what you need to do.

Wow thanks, there are no tutorials for this anywhere. Is this method 100% reliable. I don't understand the part about reading each byte by the integer representing it. Can someone elaborate?
Quote:Original post by rnw159Screw game maker.There's your sig quote for the forums :D[/quote]

Hmm looking at your code there are a few fundemental things wrong with your implementation.. Assuming this is all win32 sockets:

Firstly the call to "recv" does not guarantee to read the entire packet as you're expecting it to. You need to encapsulate it within a loop that reads from the incoming stream until the expected amount of bytes are read as indicated by the return value of the function call.

Same applies to the call to send - it doesn't guarantee that the whole packet is sent within 1 call.

Next simply reading and writing to the packet structure as a buffer is a very bad way of doing things for a number of reasons - but foremost it's highly likely byte ordering on the recipient end will probably not be as you expect it to. If you're doing things with floating point values then you're really asking for trouble (look up nthol and htonl and others). Attempt to write a manual serialization for each data type within the packet. Also be aware that byte packing may be different the client/server so the sizeof(packet) may return different values.

There are probably a few more issues but I hope that sets you in the right direction.



Yes I agree. You have a very insightful post my friend.

Wow thanks, there are no tutorials for this anywhere. Is this method 100% reliable. I don't understand the part about reading each byte by the integer representing it. Can someone elaborate?


This is mostly an issue when crossing platform or language boundaries.

Generally if they are all the same platform and you are only transferring plain data you can pass that data just fine. If you are mixing clients, such as a Java client and a C++ client, or mixing platforms such as PC and Mac, then this becomes very important.
What Striken said is correct, but, I don't think that's causing you any issues.

In fact, assuming you are sending this data from one machine of the same type to another machine, I wouldn't waste my time worry about endian-ness. Just sending and receiving the data as you have it (by casting the structure to a char *, when sending and receiving) will be fine. FWIW, if you send data from a little-endian machine, and recieve it on a little endian machine, the data will come out right, and there's not need to swap 'em twice.

I think this is related to these sockets being non-blocking. Are you calling "select" to ensure the socket is ready to "send" and it's ready to "recv"? If you're calling recv on a non-blocking socket, and there's no data, it'll return 0. You need to make sure there's data available first by calling "select". You should, technically, also check your socket is ready to send by calling select, but that's rarely

BTW, why are you non-blocking?

Hmm looking at your code there are a few fundemental things wrong with your implementation.. Assuming this is all win32 sockets:

Firstly the call to "recv" does not guarantee to read the entire packet as you're expecting it to. You need to encapsulate it within a loop that reads from the incoming stream until the expected amount of bytes are read as indicated by the return value of the function call.

Same applies to the call to send - it doesn't guarantee that the whole packet is sent within 1 call.

Next simply reading and writing to the packet structure as a buffer is a very bad way of doing things for a number of reasons - but foremost it's highly likely byte ordering on the recipient end will probably not be as you expect it to. If you're doing things with floating point values then you're really asking for trouble (look up nthol and htonl and others). Attempt to write a manual serialization for each data type within the packet. Also be aware that byte packing may be different the client/server so the sizeof(packet) may return different values.

There are probably a few more issues but I hope that sets you in the right direction.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)


If you're calling recv on a non-blocking socket, and there's no data, it'll return 0.


Actually, it will return -1 with errno set to EWOULDBLOCK on Linux, WSAEWOULDBLOCK on Windows.


enum Bool { True, False, FileNotFound };
I'm using non blocking because I'm sending game data. If select is what I need then I will look into it. Thanks!

*edit*
I have a new problem. The program works great in terms of successfully sending information. However, there is about a 10 second lag when I'm trying to send realtime information. This is being sent over local 127.0.0.1. The server takes a long time to send the data after it has been received. and the client takes a long time to send the data to the server.
Quote:Original post by rnw159Screw game maker.There's your sig quote for the forums :D[/quote]

This topic is closed to new replies.

Advertisement