[ENet-discuss] discerning disconnect reasons
Kevin Wasserman
kwasserman at maddocsoftware.com
Tue Oct 5 11:52:50 PDT 2004
Nick Hodapp wrote:
>Is it possible to determine how a peer was disconnected?
That is, when
>net_host_service() raises a Disconnect event, I want to
know whether the
>peer disconnected on its own, whether it timed out, or
whether I
>disconnected it earlier with a call to
enet_peer_disconnect().
I had the same desire; it was not too difficult to
implement. I added the following enum to enet.h:
typedef enum
{
ENET_DISCONNECT_ABNORMAL = 0, // disconnect due to
abnormal behaviour
ENET_DISCONNECT_REMOTE = 1, // 'normal' gentle
disconnect resulting from a remote call to
enet_peer_disconnect()
ENET_DISCONNECT_HARD = 2, // 'hard' disconnect
resulting from peer's call to enet_peer_disconnect_now()
} ENetPeerDisconnectReason;
and to the ENetPeer struct I added (after the mtu, so the
size is unchanged assuming your structs are 4-byte aligned)
enet_uint16 disconnectReason; /**< when a peer
is disconnected, this is the reason; it is used to generate
the appropriate event before enet_peer_reset() is called*/
in ENetEventType I replaced ENET_EVENT_TYPE_DISCONNECT with
the following:
/** a peer has disconnected. This event is generated on
a successful
* completion of a disconnect initiated by a local call
to
* enet_peer_disconnect. The peer field contains the
peer which
* disconnected, which will already have been reset.
*/
ENET_EVENT_TYPE_LOCAL_DISCONNECT = 2,
/** remote peer disconnected via enet_peer_disconnect()
*/
ENET_EVENT_TYPE_REMOTE_DISCONNECT = 4,
/** a remote peer has disconnected via
enet_peer_disconnect_now()
*/
ENET_EVENT_TYPE_HARD_DISCONNECT = 5,
/** a peer has been disconnected due to a reliable packet
timeout
*/
ENET_EVENT_TYPE_TIMEOUT_DISCONNECT = 6,
/** a peer has been disconnected due to abnormal
behaviour
*/
ENET_EVENT_TYPE_ABNORMAL_DISCONNECT = 7,
in peer.c I initialize disconnectReason to
ENET_DISCONNECT_ABNORMAL in enet_peer_reset():
peer -> disconnectReason = ENET_DISCONNECT_ABNORMAL;
there are numerous places in ENet where unexpected behaviour
by
the peer results in the peer state being switched to ZOMBIE;
if
this happens we will generate an abnormal disconnect event
in enet_protocol_dispatch_incoming_commands() (see below).
in enet_peer_disconnect() I made the following changes:
command.header.flags = 0;
command.header.commandLength = sizeof
(ENetProtocolDisconnect);
if (peer -> state == ENET_PEER_STATE_CONNECTED)
command.header.flags |=
ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
becomes
/* Ask for an ack even if we're not connected; it'll
just be thrown away in that case. */
/* If we don't ask for an ack, the receiver will
flag it as a 'hard' disconnect a la
enet_peer_disconnect_now(). */
command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
command.header.commandLength = sizeof
(ENetProtocolDisconnect);
btw, while your in peer.c, there's a typo in
enet_peer_disconnect_now() that I should really mention in a
separate post:
if (peer -> state != ENET_PEER_STATE_DISCONNECTED)
return;
should be
if (peer -> state == ENET_PEER_STATE_DISCONNECTED)
return;
I generally only ever call enet_peer_disconnect_now() in my
exception handler.
This way, when a peer gets a 'hard' disconnect, it knows the
other peer crashed.
in protocol.c I made the following change to
enet_protocol_dispatch_incoming_commands():
if (currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
{
host -> recalculateBandwidthLimits = 1;
event -> type = ENET_EVENT_TYPE_DISCONNECT;
becomes
if (currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
{
// Disconnect zombies at this point, generating
the appropriate event
host -> recalculateBandwidthLimits = 1;
switch ( currentPeer->disconnectReason )
{
case ENET_DISCONNECT_ABNORMAL:
event->type =
ENET_EVENT_TYPE_ABNORMAL_DISCONNECT;
break;
case ENET_DISCONNECT_REMOTE:
event->type =
ENET_EVENT_TYPE_REMOTE_DISCONNECT;
break;
case ENET_DISCONNECT_HARD:
event->type =
ENET_EVENT_TYPE_HARD_DISCONNECT;
break;
}
in enet_protocol_handle_disconnect():
if (command -> header.flags &
ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
peer -> state =
ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT;
else
peer -> state = ENET_PEER_STATE_ZOMBIE;
becomes
if (command -> header.flags &
ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
peer -> state =
ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT;
else
{
peer -> disconnectReason = ENET_DISCONNECT_HARD;
peer -> state = ENET_PEER_STATE_ZOMBIE;
}
in enet_protocol_handle_acknowlegement():
case ENET_PEER_STATE_DISCONNECTING:
if (commandNumber !=
ENET_PROTOCOL_COMMAND_DISCONNECT)
return 0;
host -> recalculateBandwidthLimits = 1;
event -> type = ENET_EVENT_TYPE_DISCONNECT;
event -> peer = peer;
enet_peer_reset (peer);
return 1;
becomes
case ENET_PEER_STATE_DISCONNECTING:
if (commandNumber !=
ENET_PROTOCOL_COMMAND_DISCONNECT)
return 0;
host -> recalculateBandwidthLimits = 1;
event -> type = ENET_EVENT_TYPE_LOCAL_DISCONNECT;
event -> peer = peer;
enet_peer_reset (peer);
return 1;
in enet_protocol_send_acknowlegement():
if (acknowledgement -> command.header.command ==
ENET_PROTOCOL_COMMAND_DISCONNECT)
peer -> state = ENET_PEER_STATE_ZOMBIE;
becomes
if (acknowledgement -> command.header.command ==
ENET_PROTOCOL_COMMAND_DISCONNECT)
{
peer -> disconnectReason =
ENET_DISCONNECT_REMOTE;
peer -> state = ENET_PEER_STATE_ZOMBIE;
}
and finally, in enet_protocol_check_timeouts():
if (outgoingCommand -> roundTripTimeout >=
outgoingCommand -> roundTripTimeoutLimit)
{
event -> type = ENET_EVENT_TYPE_DISCONNECT;
event -> peer = peer;
enet_peer_reset (peer);
return 1;
}
becomes
if (outgoingCommand -> roundTripTimeout >=
outgoingCommand -> roundTripTimeoutLimit)
{
event -> type =
ENET_EVENT_TYPE_TIMEOUT_DISCONNECT;
event -> peer = peer;
enet_peer_reset (peer);
return 1;
}
OK, so it was a little more involved than I remembered.
I'm also not really happy with having five separate
disconnect events; it would probably be better to
have a separate field in ENetEvent for the disconnect
reason or something.
-Kevin Wasserman
More information about the ENet-discuss
mailing list