Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [Networking] ENet Problem

This thread is locked; no one can reply to it. rss feed Print
[Networking] ENet Problem
Derezo
Member #1,666
April 2001
avatar

I have a server application and a client application which, for now, just do a login procedure together. I send 43 bytes worth of login information to the server (20 bytes for a name, 20 bytes for a password, 1 byte for error, state and type). It is in the order of type, name, password, state, error. The server unpacks the packet into the same object that the client uses, validates the information and sends an updated packet with the same structure back to the client. It worked flawlessly until I added validation, additional states and made the errors more specific.
For some strange reason the packet does not arrive as the client had sent it. The first 21 (possibly less) bytes are corrupt, but are predictable. The first byte of the first packet starts at 184 and the next packet is always 112. Then the next is 184, and it switches like that constantly. The name (which starts at byte 2) always appears as '`š'. On the client I log the packet immediately before the call to enet_peer_send() like so:

  TRACE("Connection::Send() - Sending packet %p (%u bytes, 1st byte: %u)\n",_packet,_packet->dataLength,*(_packet->data));
  enet_peer_send(peer,0,_packet);

The packet type of 1 is always logged (which is correct). Like so:

Connection::Send() - Sending packet 01DA6428 (43 bytes, 1st byte: 1)

Similarly, on the server, I check the first byte of the packet before I do anything else with it:

        switch (e.type)
        {
    case ENET_EVENT_TYPE_RECEIVE:
      TRACE("A packet of length %u was received from %s. The first byte is : %u\n",
        e.packet -> dataLength,ipStr,*(e.packet->data));
        // ...

But the server logs a different type!

A packet of length 43 was received from 206.186.35.60. The first byte is : 184

Gah!!
I'm pulling my hair out :(

There is a lot of code in total, but here is the main server code, which I think is where the problem is:

#SelectExpand
1#include <allegro.h> 2#include <winalleg.h> 3#include "server.h" 4#include "networkcontrol.h" 5 6Server::Server() 7{ 8 if (enet_initialize () != 0) 9 { 10 TRACE("Error initializing enet.\n"); 11 return; 12 } 13 14 /* Bind the server to the default localhost. */ 15 address.host = ENET_HOST_ANY; 16 /* Bind the server to port 4000. */ 17 address.port = 4000; 18 19 server = enet_host_create (& address,32,0,0); 20 if(server == NULL) 21 { 22 TRACE("Unable to create host object!\n"); 23 } 24 else 25 TRACE("Successfully initialized enet server.\n"); 26} 27 28void Server::Listen() 29{ 30 ENetEvent e; 31 unsigned char packet_type = 0; 32 char ipStr[20]=""; 33 Player *_player; 34 35 while (enet_host_service (server, & e, 0) > 0) 36 { 37 IpToStr(ipStr,e.peer->address.host); 38 switch (e.type) 39 { 40 case ENET_EVENT_TYPE_CONNECT: 41 TRACE("A new client connected from %s:%u.\n", 42 ipStr, 43 e.peer -> address.port); 44 45 // add the player to the list. 46 _player = new Player(); 47 _player->SetState(SERVER_PLAYERSTATE_LOBBY); // the player is in the lobby and not yet logging in 48 _player->c = new Connection(e.peer); 49 players.push_back(_player); 50 break; 51 case ENET_EVENT_TYPE_RECEIVE: 52 TRACE("A packet of length %u was received from %s. The first byte is : %u\n", 53 e.packet -> dataLength,ipStr,*(e.packet->data)); 54 55 // get the player who sent it 56 std::list <Player *>::iterator i; 57 for(i = players.begin();i!=players.end()&&!players.empty();i++) 58 { 59 if((*i)->c->peer == e.peer) 60 { 61 _player = *i; 62 break; 63 } 64 } 65 if(!_player) 66 { 67 TRACE("Error: Peer is not attached to a player object. Discarding packet.\n"); 68 break; 69 } 70 71 // get the type of packet 72 packet_type = *(e.packet->data); 73 74 switch(packet_type) 75 { 76 case NC_LOGIN: 77 { 78 if(!_player->loginControl) _player->loginControl = new LoginControl(0,0); 79 _player->loginControl->Unpack(e.packet); 80 TRACE("Player Login [%s|%s] %s|%i\n",_player->loginControl->name,_player->loginControl->passwd,ipStr,_player->loginControl->state); 81 82 if(!_player->loginControl->Validate()) 83 { 84 TRACE("LOGIN FAILED! Unable to validate user (error code %i).\n",_player->loginControl->err); 85 } 86 // send result back 87 _player->loginControl->Pack(); 88 _player->loginControl->Synchronize(_player->c); 89 90 break; 91 } 92 default: 93 { 94 if(!_player->loginControl) _player->loginControl = new LoginControl(0,0); 95 _player->loginControl->Unpack(e.packet); 96 TRACE("Player Login [%s|%s] %s|%i\n",_player->loginControl->name,_player->loginControl->passwd,ipStr,_player->loginControl->state); 97 98 if(!_player->loginControl->Validate()) 99 { 100 TRACE("LOGIN FAILED! Unable to validate user (error code %i).\n",_player->loginControl->err); 101 } 102 // send result back 103 _player->loginControl->Pack(); 104 _player->loginControl->Synchronize(_player->c); 105 106 break; 107 TRACE("Packet type unknown. Discarded (%u).\n",packet_type); 108 break; 109 } 110 } 111 112 /* Clean up the packet now that we're done using it. */ 113 enet_packet_destroy (e.packet); 114 break; 115 } 116 } 117} 118 119int Server::Update() 120{ 121 return 0; 122} 123 124volatile int kill_server = 0; 125 126void KillServer(void) 127{ 128 kill_server = 1; 129} 130END_OF_FUNCTION(KillServer); 131 132int main() 133{ 134 allegro_init(); 135 install_timer(); 136 install_keyboard(); 137 138 LOCK_FUNCTION(KillServer); 139 set_close_button_callback(KillServer); 140 141 Server *server = new Server(); 142 143 while(!kill_server) 144 { 145 server->Listen(); 146 while(server->Update()); 147 rest(1); 148 } 149 150 delete server; 151 return 0; 152} 153END_OF_MAIN();

Here is the connection.cpp:

#SelectExpand
1#include <allegro.h> 2#include <winalleg.h> 3#include "connection.h" 4#include "DataBank.h" 5 6#define PORT_NUM 4000 7 8Connection::Connection(char *_host) 9{ 10 peer = 0; 11 client = 0; 12 if(!Connect(_host)) 13 { 14 allegro_message("Error: Unable to connect to Modia server."); 15 peer = 0; 16 client = 0; 17 } 18} 19 20Connection::Connection(ENetPeer *_peer) 21{ 22 peer = _peer; 23 client = 0; 24} 25 26void Connection::Send(ENetPacket *_packet) 27{ 28 if(!peer) 29 { 30 TRACE("Connection::Send() - Invalid peer.\n"); 31 return; 32 } 33 34 if(!_packet) 35 { 36 TRACE("Connection::Send() - Invalid packet.\n"); 37 return; 38 } 39 40 TRACE("Connection::Send() - Sending packet %p (%u bytes, 1st byte: %u)\n",_packet,_packet->dataLength,*(_packet->data)); 41 enet_peer_send(peer,0,_packet); 42} 43 44int Connection::Update() 45{ 46 if(!peer) { TRACE("Connection::Update() - Unable to update connection. No peer.\n"); return 0; } 47 if(!client) { TRACE("Connection::Update() - Unable to update connection. No client object.\n"); return 0; } 48 ENetEvent e; 49 char ipStr[20]; 50 unsigned char packet_type = 0; 51 52 while (enet_host_service (client, &e, 0) > 0) 53 { 54 IpToStr(ipStr,e.peer->address.host); 55 switch (e.type) 56 { 57 case ENET_EVENT_TYPE_RECEIVE: 58 TRACE("A packet of length %u was received from %s.\n", 59 e.packet -> dataLength,ipStr); 60 61 // get the type of packet 62 packet_type = *(e.packet->data); 63 64 switch(packet_type) 65 { 66 case NC_LOGIN: 67 { 68 if(!DataBank::player->loginControl) 69 { 70 TRACE("Error: No login control associated with player.\n"); 71 TRACE("Login packet discarded."); 72 break; 73 } 74 DataBank::player->loginControl->Unpack(e.packet); 75 TRACE("Unpacked login data. (%s, %s, %i, %i)\n",DataBank::player->loginControl->name,DataBank::player->loginControl->passwd,DataBank::player->loginControl->state,DataBank::player->loginControl->err); 76 break; 77 } 78 default: 79 { 80 TRACE("Packet type unknown. Discarded.\n"); 81 break; 82 } 83 } 84 85 /* Clean up the packet now that we're done using it. */ 86 enet_packet_destroy (e.packet); 87 break; 88 } 89 } 90 91 return 1; 92} 93 94int Connection::Connect(char *_host) 95{ 96 TRACE("Connecting to host %s...",_host); 97 enet_address_set_host (& address, _host); 98 address.port = PORT_NUM; 99 100 ENetEvent e; 101 /* Initiate the connection, allocating the two channels 0 and 1. */ 102 client = enet_host_create (NULL,1,5,5); 103 peer = enet_host_connect(client, & address, 2); 104 105 if (peer == NULL) 106 { 107 TRACE("FAILED.\nCould not find host.\n"); 108 return 0; 109 } 110 111 /* Wait up to 5 seconds for the connection attempt to succeed. */ 112 if (enet_host_service (client, &e, 5000) > 0 && 113 e.type == ENET_EVENT_TYPE_CONNECT) 114 { 115 TRACE("Succeeded.\n"); 116 } 117 else 118 { 119 /* Either the 5 seconds are up or a disconnect event was */ 120 /* received. Reset the peer in the event the 5 seconds */ 121 /* had run out without any significant event. */ 122 enet_peer_reset (peer); 123 peer = NULL; 124 125 TRACE("FAILED. Connection reset by peer (TIMEOUT).\n"); 126 return 0; 127 } 128 return 1; 129} 130 131Connection::~Connection() 132{ 133 TRACE("Disconnecting from host..."); 134 135 TRACE("OK.\n"); 136}

If necessary, the logincontrol.cpp:

#SelectExpand
1#include <allegro.h> 2#include <winalleg.h> 3#include <string.h> 4#include "player.h" 5#include "NetworkControl.h" 6 7LoginControl::LoginControl(const char *_name,const char *_passwd) 8{ 9 type = NC_LOGIN; // set the type of the command 10 packet_out = 0; 11 if(_name && _passwd) 12 { 13 if(strlen(_name) > 20) return; 14 if(strlen(_passwd) > 20) return; 15 strcpy(name,_name); 16 strcpy(passwd,_passwd); 17 } 18 state = LOGIN_STATE_WAIT; 19 err = 0; 20 c = 0; 21 size = 43; // name + passwd + state(1) + err(1) + type(1) 22} 23 24ENetPacket *LoginControl::Pack() 25{ 26 unsigned char *buffer = new unsigned char[size+1]; 27 if(packet_out) 28 { 29 TRACE("LoginControl::Pack() - Warning. packet_out exists! overwriting packet.\n"); 30 enet_packet_destroy(packet_out); 31 packet_out = 0; 32 } 33 34 if(!buffer) return 0; 35 memset(buffer,'\0',size+1); 36 37 if(state > 3) state = 0; 38 39 // pack everything into the buffer 40 unsigned char *ptr = buffer; 41 *(ptr++) = type; 42 memcpy(ptr,name,20); // name[20] 43 ptr += 20; 44 memcpy(ptr,passwd,20); // passwd[20] 45 ptr += 20; 46 *ptr = state; 47 *(++ptr) = err; 48 49 packet_out = enet_packet_create(buffer,size,ENET_PACKET_FLAG_RELIABLE); 50 51 delete[] buffer; 52 return packet_out; 53} 54 55int LoginControl::Unpack(ENetPacket *_packet) 56{ 57 if(_packet->dataLength != size) 58 { 59 TRACE("LoginControl :: NETWORK ERROR! Data packet size invalid!\n"); 60 err = LOGIN_ERR_BADPACKET; // wrong data length 61 state = LOGIN_STATE_WAIT; // set state back to beginning. 62 63 return 0; 64 } 65 66 unsigned char *ptr = _packet->data; 67 type = *(ptr++); 68 memcpy(name,ptr,20); 69 ptr += 20; 70 memcpy(passwd,ptr,20); 71 ptr += 20; 72 state = *(ptr++); 73 err = *ptr; 74 75 TRACE("LoginControl packet has been unpacked. {Type: %i Err: %i State: %i}\n",type,err,state); 76 77 return 0; 78} 79 80int LoginControl::Validate() 81{ 82 TRACE("Validating user %s pass %s...",name,passwd); 83 // check if the username / password meets minimum criteria 84 if(strlen(name) < 3 || strlen(name) > 12) 85 { 86 SetState(LOGIN_STATE_FAILED); 87 err = LOGIN_ERR_WRONGLENGTH; 88 TRACE("LOGIN_ERR_WRONGLENGTH\n"); 89 return 0; 90 } 91 if(strlen(passwd) < 3 || strlen(passwd) > 19) 92 { 93 SetState(LOGIN_STATE_FAILED); 94 err = LOGIN_ERR_WRONGLENGTH; 95 TRACE("LOGIN_ERR_WRONGLENGTH\n"); 96 return 0; 97 } 98 99 char fbuf[255]; 100 _snprintf(fbuf,255,"players/%c/%s",name[0],name); 101 if(!exists(fbuf)) 102 { 103 // player not found 104 err = LOGIN_ERR_BADNAME; 105 SetState(LOGIN_STATE_FAILED); 106 TRACE("LOGIN_ERR_BADNAME\n"); 107 return 0; 108 } 109 110 Player *player = new Player(fbuf); 111 if(strcmp(player->passwd,passwd)) 112 { 113 err = LOGIN_ERR_BADPASSWD; 114 SetState(LOGIN_STATE_FAILED); 115 TRACE("LOGIN_ERR_BADPASSWD\n"); 116 return 0; 117 } 118 119 TRACE("Success\n"); 120 SetState(LOGIN_STATE_SUCCESS); 121 122 return state; 123}

Any help is appeciated. I have no idea what happened, it's been like this for about 3 coding sessions without any progress ><

Update
The client has the same problem when receiving from the server. The first byte interchanges between 88 and 168 every other packet :-/

"He who controls the stuffing controls the Universe"

A J
Member #3,025
December 2002
avatar

I would stop using the library.

Does the library contain any example files ?
Do they work ?
Have you tried hacking them apart and using them ?

Are you using a library for a reason ?
DIY TCP/IP isn't that hard, unless this library offers you features you want, i'd stop using it.

Is this library stable? or even complete?

___________________________
The more you talk, the more AJ is right. - ML

X-G
Member #856
December 2000
avatar

Oh my. It's almost as if someone had warned earlier that enet was a bad idea...

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Matthew Leverton
Supreme Loser
January 1999
avatar

Yes, like all good newbs, let's blame the library! I've had no problems with enet, but I don't enjoy picking through someone else's C++, so I really cannot offer any advice. Plus, Speedhack is almost upon us...

Sounds to me like you are corrupting the memory somehow with the recent changes you made.

miran
Member #2,407
June 2002

Did you recompile everything after you made changes?

--
sig used to be here

Steve++
Member #1,816
January 2002

Just some general advice - it's not a good idea to use a UDP-based library if you want to process all your packets reliably and in order. Sure, some libraries may offer some slight advantages over TCP in the way they implement that, but with UDP you also have to worry about things like NAT punch-through, especially with all the varying implementations of NAT that seem to be found on routers.

I would suggest using whatever asynchronous I/O is offered by the target OS (assuming Windows from looking at the code) or even better, a platform-independent abstraction of asynchronous TCP/IP.

Derezo
Member #1,666
April 2001
avatar

Yeah, I tried a clean make. No luck :(

Quote:

I don't enjoy picking through someone else's C++

Me either, and my code is especially spaghetti-like ;) I'm just at a total loss as to what is causing this problem. It APPEARS that I'm corrupting something, but that doesn't make sense. I am examining it going out and outputting the results, they are fine. But coming back in, on a separate application, the packet is damaged and half corrupted. That doesn't make any sense! >:(
The changes that I did make were relatively simplistic. The biggest changes were made by the addition of the Verify function on the login control... which doesn't even have any actual networking code in it. :(

Quote:

Oh my. It's almost as if someone had warned earlier that enet was a bad idea...

I thought of that after I had came across this problem. "Maybe X-G was right and enet isn't as simple as it seems.", but it was working so great on Friday, before I added user verification.
Right now I am thinking that it is enet's fault, but nothing makes any sense really. The networking code was hardly changed at all which is what really makes it strange.
If I can't solve this problem by next saturday I'll likely be switching libraries.. but none of the others are quite as simple as enet, that's the only problem :( Plain old sockets didn't work very well for my MUD engine. They did work, but I needed some sort of optimization. When a lot of users connected (a lot is 5 or more, heh) latency would drastically increase. :(

Now to answer AJ's questions:

Quote:

Does the library contain any example files ?

Yes. Google is your friend.

Quote:

Do they work ?

Yes.

Quote:

Have you tried hacking them apart and using them ?

Yes.

Quote:

Are you using a library for a reason ?

Yes.

Quote:

DIY TCP/IP isn't that hard, unless this library offers you features you want, i'd stop using it.

I've used sockets directly (if that's what you mean by do-it-yourself TCP/IP), out of the box they do not have very many features. I wouldn't recommend it, really. If you're new to networking it is a good learning experience, though.

Quote:

Is this library stable?

As far as I can tell, yes. Numerous other projects have used this library. However, I am a little new to enet and have no prior experience with it.

Quote:

or even complete?

You can visit their website and decide for yourself. Personally, it is as complete as I need it to be. You may need more features or a higher level library, but if you're looking for a networking library please create a new thread or use the search feature. It's been asked many times, so you should be able to find some results worth viewing.
[edit]

Quote:

it's not a good idea to use a UDP-based library if you want to process all your packets reliably and in order.

ENet claims to have reliable and sequenced packets. I've set the appropriate values for reliable packets. The problem is elsewhere.

"He who controls the stuffing controls the Universe"

A J
Member #3,025
December 2002
avatar

if you've testing the entry and exit points of the library and the data is getting corrupted within, its the library at fault.

unless your passing invalid length data..
when you pass the length of the data to send, what are you passing ?
is it derived from a strlen() or a fixed packet size?

->Unpack() ->Pack() what are these ? they seem like something that will modify your data

___________________________
The more you talk, the more AJ is right. - ML

Derezo
Member #1,666
April 2001
avatar

Quote:

if you've testing the entry and exit points of the library and the data is getting corrupted within, its the library at fault.

The problem is that I made changes to cause this problem... I should have kept better records of what exactly I changed :(
The problem appears to be with enet, but because it was working fine before I find it hard to accept that.

The size of the data is 43 bytes (on both ends). It is fixed. Pack() creates an ENet Packet from the data and Unpack() takes an ENet Packet and turns it into useable data. I did have trace calls at every step of the pack/unpack process and it did not show anything different. Unpack() is called after the problem is evident and Pack() is called just before sending the packet which does not show the problem happening.

Oh well. SpeedHack time.

"He who controls the stuffing controls the Universe"

Steve++
Member #1,816
January 2002

I searched the mailing list archive, but I couldn't find anyone else complaing about a data corruption problem. Let's not forget that Enet adds information (most likely at the beginning) to packets such as sequence and channel numbers. I don't know anything about Enet, but I've got a hunch this has something to do with that.

Try reducing your code to the smallest code that will reproduce the error you're experiencing.

Also try the Enet mailing list.

Go to: