Jump to content
  • Advertisement
Sign in to follow this  
graveyard filla

simple networking / enet questions.

This topic is 5410 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

hi, im working on networking my pong clone using enet. i have some questions which are probably not enet specific, but im using enet if that helps. will broadcasting a packet from client side, send the packet to himself too? what about the server, does it send the packet to the server too? does enet maintain a list of all peer's for me or something? i cant quite figure that out. theres more questions, but this should due for now. thanks a lot for any help.

Share this post


Link to post
Share on other sites
Advertisement
to add to the questions:

i know how to send data from the client to the server. this is easy because i can record the EnetPeer, since the client will only have one of these (the server). but, how do i keep track of and know all the peer's that the server has? IE, all the clients that are connected? i can get the client's peer from the enet_host_connect() call, but, the server doesnt use this function, so im not sure how to get the peer's that are connected to the server...

thanks again for any help in advance.

Share this post


Link to post
Share on other sites
I have honestly never thought to ask if a broadcast message gets sent to itself (my guess: no...might be hard to do physically, definately impossible for half-duplex cards, and broadcasts are not to be routed). Please, somebody correct me if I'm wrong...

enet has a nice (short) tutorial, with code:

link

look in the "Managing an ENet host" section.

You would store the list of connections yourself, adding them as you receive the ENET_EVENT_TYPE_CONNECT event from enet_host_service().

Share this post


Link to post
Share on other sites
thanks lonesock. so a server can call broadcast(), and it will send to all clients? and if a client calls broadcast(), it will send to all other clients, but not the server?

i've actually read through that tutorial a couple times, and have it constantly up in my browser as i code. i still dont understand how a server can manage a bunch of clients that connect to it.

for example, to send a back from the server to a client, you need a ENetPeer * as parameters in the send function. this represents the person your sending it to. where does this pointer come from exactly? how does a server get a client peer? is it when you first process a CONNECTED message? and event.peer ? but since its a pointer, it worries me if its managed internally and stuff or not. so how exactly do i do this? should i make a copy of the event.peer that i get in the CONNECT message?

also, what about the 'peers' member of the ENetHost data type? i was thinking this was the list of all peers, which enet managed for me. so its not then?

if not, what is it exactly? and if so, if the host is a server, will this array of peers be all the clients connected? and if the host is a client, will this array only contain one peer at all times, which is the server its connected to?

while you're helping me, im a little confused still on the whole message pump system. so eNet already knows who the peer is who sent a packet to me? therefore, i dont have to keep my own ID and do a lookup to find out who the sender was? upon a CONNECT message, i could fill this peer->data member with an ID, and in all further messages, this peer->data will have what i allocated? so basically, enet does the sorting of messages and identifying of sender for me. of course, keeping an ID member is still usefull im sure.

thanks a lot for all your help!

Share this post


Link to post
Share on other sites
part answer, and part bump, [8^)

You're probably farther along than I in implementing enet...I have finished my research (survey is more like it) considering features, licenses, etc.

However, a good link might be cube, the open source game for which enet was originally developed. So in that source you should find a fairly extensive implementation [8^). Hope this helps, and sorry I can't be more helpful myself. Searching gamedev, there are a few people who have actually used it in code already (bmm) and I remember a post by a moderator, though I can't find it now. PMing them might work.

Share this post


Link to post
Share on other sites
Please note that I am by no means particularly experienced in this department so I apologize if I have something drastically wrong…

Quote:

thanks lonesock. so a server can call broadcast(), and it will send to all clients? and if a client calls broadcast(), it will send to all other clients, but not the server?


When an Enet host calls broadcast() it sends the packet to all connected peers. In the case of the server, it sends the packet to each client. The only peer a client is connected to is the server – usually there is no direct connection between the separate clients. Therefore, by calling broadcast() on a client you are just sending the packet to the server… may as well just use send() and host.peers[ 0 ].

Quote:

i've actually read through that tutorial a couple times, and have it constantly up in my browser as i code. i still dont understand how a server can manage a bunch of clients that connect to it.

for example, to send a back from the server to a client, you need a ENetPeer * as parameters in the send function. this represents the person your sending it to. where does this pointer come from exactly? how does a server get a client peer? is it when you first process a CONNECTED message? and event.peer ? but since its a pointer, it worries me if its managed internally and stuff or not. so how exactly do i do this? should i make a copy of the event.peer that i get in the CONNECT message?

also, what about the 'peers' member of the ENetHost data type? i was thinking this was the list of all peers, which enet managed for me. so its not then?
if not, what is it exactly? and if so, if the host is a server, will this array of peers be all the clients connected? and if the host is a client, will this array only contain one peer at all times, which is the server its connected to?



You can send a packet back to a host that has sent a packet to you by using event.peer. Alternatively you can use the peers[] property of your ENetHost. The peers[] member is a static array that contains slots for all the peers that might ever be connected – that is, the number you specify as the maximum peer count when you initialize the host. This I found rather inconvenient as I am unsure how this array is handled and sorted within Enet. I also found the ENetHost.peerCount particularly useless as it only returns the maximum number of peers that you specified earlier (which should be 1 for a client).
You can transverse through the peers[] array and determine which slots actually have connected hosts by checking if peers[ i ].address.host != 0. ( If there is no connection on a specific slot this will be 0 – Enet handles this internally. )

Quote:

while you're helping me, im a little confused still on the whole message pump system. so eNet already knows who the peer is who sent a packet to me? therefore, i dont have to keep my own ID and do a lookup to find out who the sender was? upon a CONNECT message, i could fill this peer->data member with an ID, and in all further messages, this peer->data will have what i allocated? so basically, enet does the sorting of messages and identifying of sender for me. of course, keeping an ID member is still usefull im sure.

thanks a lot for all your help!


That is basically correct. If you wish to use the peer[i ]->data pointed I suggest you, at program initialization, allocate a certain amount of memory for each possible peer. Remember to deallocate this at the end! Alternatively, you could just create a static array and set each peer[ i ]->data pointer to point to the array’s elements as you please, thus saving you the trouble of allocating memory at runtime.
You can identify a peer using its address.host value combined with its address.port value. However, this is a rather inefficient way to identify things because that is 6 bytes combined and you always want to send as little data as possible over a network. Unless you plan on allowing more that 256 players, you can ID clients using a single byte. If that is not enough you can allow for 65536 clients using two bytes per ID. Also note that upon reciving a disconnect message, the even.peer.host and port will be 0, making it difficult to deal with disconnections if you use that to identify clients.
In short, I’d think that using peer->data to store the ID of clients is a good use. Then, upon receiving a packet telling you that player X has moved to Z, you can search through your array/list/means_of_storage and find the player with the ID of X and update the position.

Hope this helps,
Jackson Allan

Share this post


Link to post
Share on other sites
thanks ALOT jack!!!! you cleared up a lot of things for me... i also have your chat program up as i code, studying it.. i've been going crazy because i cant seem to do anything but get a connection up, i couldnt seem to get packets to send, but i've been doing it completely wrong... time to go back into the code...

thats really sweet how eNet manages all the connections for me and stuff, wow this API is really nice. too bad the docs arent as fullfilling, but i think i can figure the rest out now.

there's just one last question i have (for now [smile]). i've noticed, on the server, when someone connected, i do a cout << peer->address.port and peer->address.host. these numbers are very strange, they come back as something like

16777343 3470 (host) (port)

why am i getting these strange numbers? what does this host number mean exactly? i would think it would give me their IP or something...

same with port, that doesnt make sence because the port im using is 1234 when i create the connection. so i dont know why its coming back at that. i also noticed that the port value increases for each new client, IE, the second client to connect would have a value of 3470...

one last thing, i notice you use MAX_PATH in your char arrays. i cant seem to figure out where this is #defined. what header is this from exactly? and is there any special reason to use it?

thanks again!!

Share this post


Link to post
Share on other sites
Quote:

there's just one last question i have (for now ). i've noticed, on the server, when someone connected, i do a cout << peer->address.port and peer->address.host. these numbers are very strange, they come back as something like

16777343 3470 (host) (port)

why am i getting these strange numbers? what does this host number mean exactly? i would think it would give me their IP or something...

same with port, that doesnt make sence because the port im using is 1234 when i create the connection. so i dont know why its coming back at that. i also noticed that the port value increases for each new client, IE, the second client to connect would have a value of 3470...


That IS the client’s IP and port. Firstly, a program does not understand an IP address in the format of X.X.X.X where X is a number from 0 to 255 – the . character means that the IP would have to be stored as a string and that is not going to be very useful. peer->address.host is a 4 byte version of a client’s IP. This is why you have to use the enet_address_set_host() function to pass a string as an IP – Enet then converts it for you.
Secondly – that IS the client’s port. While you do run the server on port 1234, your clients chose the first open port available to run on. This is a_ok – generally you only want to know the server port because the clients must connect to it.

eHost = enet_host_create ( 0, 1, 0, 0 );

The first parameter, if I’m not mistaken, is the port you wish to run on. As you can see, when creating a client we simply give 0 as the port which tells Enet to pick any old open port for us and use it. When creating the server we pass 1234 as the port and hope that it is not taken – then the clients assume the server is running on 1234 and try to connect.

And finally – you may wonder about the IP address the clients use by default: 127.0.0.1. This IP is actually a reserved value that, when used, circles back to the local machine. It is not your computers internet IP, it is just a dummy IP that gives you your computer.

Quote:

one last thing, i notice you use MAX_PATH in your char arrays. i cant seem to figure out where this is #defined. what header is this from exactly? and is there any special reason to use it?

thanks again!!


Ack! MAX_PATH (refers to the maximum length of a filename) is defined somewhere in windows.h or one of it’s includes and if I was to go back and fix that program up that would be one of the first things I would change – just replace all MAX_PATHs with some number you define yourself. To list some other poor qualities of the code I posted:

*If the conversation is long enough the message buffer will overflow and the program will crash.
*Clients are identified by their IP and port… although this is not all that relevant in a chat program.
*Bad/misleading commenting – this was my play around with Enet and I never suspect someone else would be reading the code :P.

When you see bluntly stupid comments like this you know to ignore them:
/* Wait up to 1000 milliseconds for an event. */
while( enet_host_service ( eHost, &event, 0 ) > 0 )

*Other things of which I cannot remember without taking another look.

Happy to help,
Jackson Allan

Share this post


Link to post
Share on other sites
hi jack,

thanks again for all your help. it sounds cheezy, but if it wasnt for you i'd probably have gotten nowhere so far [smile]. anyway, onto the Q's:
Quote:
Original post by jack_1313
That IS the client’s IP and port. Firstly, a program does not understand an IP address in the format of X.X.X.X where X is a number from 0 to 255 – the . character means that the IP would have to be stored as a string and that is not going to be very useful. peer->address.host is a 4 byte version of a client’s IP. This is why you have to use the enet_address_set_host() function to pass a string as an IP – Enet then converts it for you.
Secondly – that IS the client’s port. While you do run the server on port 1234, your clients chose the first open port available to run on. This is a_ok – generally you only want to know the server port because the clients must connect to it.


i've actually taken a networking class in school (college). we covered IP addressing and the OSI model and stuff. i understand that an IP address is nothing more then 4 bytes of binary data. but how do i convert a number like 16777343 into an IP adress? i mean, how is this stored internally exaclty so i could convert it to a human readable IP ? i should be able to convert from a decimal to binary IP adress, and vise versa (at least by hand), but what is this format that they are giving me the address as?

also, im having a problem sending data. in your code, when you want to send some text, you do this:


char data[ MAX_PATH ] = "";
enet_uint8 packettype = MSGID_TEXT;

memcpy( data, &packettype, sizeof( enet_uint8 ) );
memcpy( data + sizeof( enet_uint8 ), str, strlen( str ) );

/* Create a reliable packet of size 7 containing "packet\0" */
ENetPacket * packet = enet_packet_create( data,
strlen(data)+ 1,
ENET_PACKET_FLAG_RELIABLE );


enet_peer_send( &eHost->peers[ 0 ], 0, packet );

enet_host_flush( eHost );









now, im doing the same EXACT thing, but the packet is not getting received properly on the other end. the string just comes up as empty on the other side. my code looks like this:


char data[MAX_PATH] = "";

data[0] = HI_MY_NAME_IS;

strcpy(&data[1],name);

/* Create a reliable packet of size 7 containing "packet\0" */
ENetPacket *packet = enet_packet_create( data,
strlen(data)+1,
ENET_PACKET_FLAG_RELIABLE );


enet_peer_send(&client->peers[0], 0, packet );

enet_host_flush(client);









while im copying the data in differently, it *should* be doing the exact same thing. why is yours working but mine not? i tried using memcpy instead of the way i do it, but the same results happen.... also, im pretty sure on the server side we are doing it the same way too. on the server, i receive that packet like this:


char str[MAX_PATH] = "";
memcpy(str, event.packet->data + SIZEOF_UINT8, event.packet->dataLength - SIZEOF_UINT8);


so, any idea why my way isnt working? if i simply do strlen(name)+2 as the packet size in the call to create the packet, it works (as expected). so why wont doing strlen(data)+1 work then?

the next problem i had was a strange bug that was making my server crash. basically, when i received a packet, i would bounce that packet right back to the other clients. the problem was, this would make the program crash when i called enet_packet_destroy(). i found the solution - instead of sending the packet directly back to the other clients, i instead copy the packet's data into a new packet, and sent that new packet. this stopped it from crashing, but i dont understand why. why would bouncing back a packet that i received cause this crash? heres psuedo code of the code that was crashing:


case ENET_EVENT_TYPE_RECEIVE:
//we received SOME_MESSAGE, so lets send this message back to the other peer's

for(int i = 0; i = server->peerCount; i++)
{
if(server.peers.address == 0)
continue;

if(sender_id != server.peers ID)
{
enet_peer_send(&server_info->peers,0,event.packet);
enet_host_flush(server_info);
}
}




this is on the server. see how i receive a packet, and then send this packet right back to all the other peer's ?? this makes the server crash at the call to enet_packet_destroy().

however, if intead of sending the event.packet in the call to enet_peer_send, i simply make a new char[], copy event.packet's data into it, create a new packet with that data, and send it, it works fine!! pretty weird, i hope you know what's up with that [smile].


thanks again for all your help.

Share this post


Link to post
Share on other sites
Ok next round:

Quote:

i've actually taken a networking class in school (college). we covered IP addressing and the OSI model and stuff. i understand that an IP address is nothing more then 4 bytes of binary data. but how do i convert a number like 16777343 into an IP adress? i mean, how is this stored internally exaclty so i could convert it to a human readable IP ? i should be able to convert from a decimal to binary IP adress, and vise versa (at least by hand), but what is this format that they are giving me the address as?


One can set the IP address of an ENetAddress instance using the function enet_address_set_host(). I am assuming the conversion can be reversed using enet_address_get_host(), which requires you to pass in a char pointer. Presumably this then gives the IP address as a string (X.X.X.X) that you can then print on screen for the user’s benefit. However, I’ve not used that function so I cannot be sure – I’m just basing this on logic here :). The question mark is that, if I’m not mistake, you can set an IP address to a url such as something like “www.gameserver.net” – what does enet_address_get_host() return in this circumstance? I’m guessing the IP, but am not entirely sure.

Quote:

now, im doing the same EXACT thing, but the packet is not getting received properly on the other end. the string just comes up as empty on the other side. my code looks like this:


I suspect you have defined HI_MY_NAME_IS as 0? If so, that is your problem. The issue lies here:

ENetPacket *packet = enet_packet_create( data, strlen(data)+1, ENET_PACKET_FLAG_RELIABLE );

There second parameter is obviously the size of the data inside the packet. strlen() returns the length of a string, which is presumably the size of our data. Not so. Problem is that strlen() simply transverses through each character in the ‘string’, finds the first NULL character, and assumes that is the end.
Coincidently I you’ve (probably) defined HI_MY_NAME_IS as 0. strlen() looks at the first character in the data buffer, finds that it is 0 (ie NULL) and returns that the size is zero – string is empty.
However, to send the data you use strlen( data ) + 1. That equals out to 1 – all you end up sending is the first byte in the data buffer. Which so happens to be set as HI_MY_NAME_IS – you still receive the packet type on the other end but there is not string to accompany it! Very misleading.
To correct this problem you could simply define HI_MY_NAME_IS as anything other that 0 (but below 256 ). That would be a cheap fix. Using strlen in the way that we have is pretty poor practice. Instead, I suggest you change strlen( data ) + 1 to strlen( name ) + 2. We add two because the message type takes up one byte and we send an extra character at the end, even though we don’t really need to (you might like to change that), to terminate the ‘name’ string.

>>Oooops… just reread your post and discovered that you alreadly know that the problem involved strlen( data ) + 1. Sorry about the rambling then…

On a side note: one really does not need to use 100 messy sizeof() calls in the way that I have. Instead, because we usually know the size of certain data types, it is simpler and much clearer if you just write the size of the data. A byte, char, uint8 or anything with an 8 at the end is obviously 1 byte. Unit16 and short is 2. Uint32 is 4, as is float and int– although this last one can occasionally differ from system to system. Etc etc.

Quote:

the next problem i had was a strange bug that was making my server crash. basically, when i received a packet, i would bounce that packet right back to the other clients. the problem was, this would make the program crash when i called enet_packet_destroy(). i found the solution - instead of sending the packet directly back to the other clients, i instead copy the packet's data into a new packet, and sent that new packet. this stopped it from crashing, but i dont understand why. why would bouncing back a packet that i received cause this crash? heres psuedo code of the code that was crashing:


The problem was that enet_peer_send() automatically takes care of the de-allocation and destruction of the packet you pass into it. While this can be convenient, I found that it assumed a little too much and caused trouble.
Therefore, when you then use enet_packet_destroy() to clear the packet received Enet, instead of checking to make sure the packet is valid before attempting to clear, spits one at you and the application crashes. I too found that it was necessary to copy the packet before sending.
In copying the packet you do not actually need to create a new char array. Just create the packet with the data and size information from the received packet:

ENetPacket * packet = enet_packet_create( event.packet->data, event.packet->dataLength, ENET_PACKET_FLAG_RELIABLE );

Hopefully this has clear some things up,
Jackson Allan

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.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!