Jump to content
  • Advertisement
Sign in to follow this  
rnw159

Sending and receiving structs (I read FAQ)

This topic is 2602 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts



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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites
Posted · Hidden
Hidden

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.

Share this post


Link to post

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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.


Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!