Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Don't forget to read Tuesday's email newsletter for your chance to win a free copy of Construct 2!


Sending and receiving structs (I read FAQ)


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 rnw159   Members   -  Reputation: 120

Like
0Likes
Like

Posted 04 May 2011 - 05:55 AM


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]

Sponsor:

#2 npri   Members   -  Reputation: 107

Like
0Likes
Like

Posted 04 May 2011 - 06:10 AM

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.

#3 rnw159   Members   -  Reputation: 120

Like
0Likes
Like

Posted 04 May 2011 - 06:25 AM

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]

#4 npri   Members   -  Reputation: 107

Like
0Likes
Like

Posted 04 May 2011 - 06:48 AM

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.

#5 rnw159   Members   -  Reputation: 120

Like
0Likes
Like

Posted 04 May 2011 - 09:04 AM

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]

#6 frob   Moderators   -  Reputation: 22240

Like
0Likes
Like

Posted 04 May 2011 - 10:11 AM

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.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I write about assorted stuff.


#7 BeerNutts   Crossbones+   -  Reputation: 2978

Like
0Likes
Like

Posted 04 May 2011 - 03:02 PM

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)

#8 hplus0603   Moderators   -  Reputation: 5532

Like
0Likes
Like

Posted 04 May 2011 - 03:22 PM

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 };

#9 rnw159   Members   -  Reputation: 120

Like
0Likes
Like

Posted 04 May 2011 - 09:32 PM

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]

#10 npri   Members   -  Reputation: 107

Like
0Likes
Like

Posted 05 May 2011 - 02:37 AM

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.


One cause may be if you're sending many small messages over TCP, they may not actually be transmitted until the outgoing stream buffer is of a certain size.

setsocketopt - Check out TCP_NODELAY
- Teach a programmer an answer, he can code for a day. Show a programmer the documentation, he can code for a lifetime.

#11 rnw159   Members   -  Reputation: 120

Like
0Likes
Like

Posted 05 May 2011 - 07:09 AM


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.


One cause may be if you're sending many small messages over TCP, they may not actually be transmitted until the outgoing stream buffer is of a certain size.

setsocketopt - Check out TCP_NODELAY


Thanks this helped a lot! The problem I have now is that if I continually send data the lag gets greater and greater until I stop sending data for a few seconds. CAn you offer some help on this?

*edit*
Also it gets twice as laggy for every client I add. Any suggestions?

Quote:

Original post by rnw159Screw game maker.

There's your sig quote for the forums :D[/quote]

#12 hplus0603   Moderators   -  Reputation: 5532

Like
0Likes
Like

Posted 05 May 2011 - 11:58 AM

Thanks this helped a lot! The problem I have now is that if I continually send data the lag gets greater and greater until I stop sending data for a few seconds. CAn you offer some help on this?


The server needs to drain all available incoming data for each iteration. This may include more than one packet from each client.
Also, because it's TCP, you may receive half packets -- see the FAQ.
Typically, you will create a buffer of some number of kilobytes per client, and then try to recv() that buffer full in each call to recv(). Then copy data out of there for your struct.

struct ClientBuf {
  size_t count;
  char buf[8192];

  void poll(int socket) {
    if (count == 8192) { return; } // already full
    int size = 8192-count;

    int r = recv(socket, &buf[count], size, 0);
    if (r > 0) { count += r; }
  }

  bool consume_data(void *dst, size_t size) {
    if (count < size) { return false; }
    memcpy(dst, buf, size);
    memmove(buf, &buf[size], count-size);
    count -= size;
    return true;
  }
};

This code assumes that the socket is non-blocking, or that select() has told you there is data to receive. Call poll() once per frame per socket that's readable (or non-blocking). Then, call consume_data() to consume data from the incoming connection, assuming sufficient data is available. Remember: There may be more than one packet in the buffer.
enum Bool { True, False, FileNotFound };

#13 dpadam450   Members   -  Reputation: 933

Like
0Likes
Like

Posted 06 May 2011 - 04:12 PM

The lag is caused by 2 things. 1 sending packets over and over. Put a time like 15 ms between sending game data. So only send 1 packet per person every 15 ms.

2. Make sure that you call receive until you have no more data on the network. If not, you will get 2 messages, and read 1. next frame you get 2 more messages (3 in your 'inbox). Read one and get to more you now have 5 messages. So just make sure to loop until there is no more data.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS