diff --git a/Documentation/Documentation.chm b/Documentation/Documentation.chm
index e3b9979..00ec493 100644
Binary files a/Documentation/Documentation.chm and b/Documentation/Documentation.chm differ
diff --git a/Lidgren.Network/NetConnection.Handshake.cs b/Lidgren.Network/NetConnection.Handshake.cs
index bdfa5c4..b930eb0 100644
--- a/Lidgren.Network/NetConnection.Handshake.cs
+++ b/Lidgren.Network/NetConnection.Handshake.cs
@@ -11,12 +11,15 @@ namespace Lidgren.Network
internal bool m_connectionInitiator;
internal string m_disconnectMessage;
internal NetIncomingMessage m_remoteHailMessage;
+ internal float m_lastHandshakeSendTime;
+ internal int m_handshakeAttempts;
///
/// The message that the remote part specified via Connect() or Approve() - can be null.
///
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
+ // heartbeat called when connection still is in m_handshakes of NetPeer
internal void UnconnectedHeartbeat(float now)
{
m_peer.VerifyNetworkThread();
@@ -33,25 +36,50 @@ namespace Lidgren.Network
// reconnect
ExecuteDisconnect("Reconnecting", true);
break;
+
case NetConnectionStatus.InitiatedConnect:
// send another connect attempt
- SendConnect();
+ SendConnect(now);
break;
+
case NetConnectionStatus.Disconnected:
throw new NetException("This connection is Disconnected; spent. A new one should have been created");
case NetConnectionStatus.Disconnecting:
// let disconnect finish first
- return;
+ break;
+
case NetConnectionStatus.None:
default:
- SendConnect();
+ SendConnect(now);
break;
}
return;
}
- // TODO: handle dangling connections
+ if (now - m_lastHandshakeSendTime > m_peerConfiguration.m_resendHandshakeInterval)
+ {
+ if (m_handshakeAttempts > m_peerConfiguration.m_maximumHandshakeAttempts)
+ {
+ // failed to connect
+ ExecuteDisconnect("Failed to establish connection - no response from remote host", true);
+ return;
+ }
+
+ // resend handshake
+ switch (m_status)
+ {
+ case NetConnectionStatus.InitiatedConnect:
+ SendConnect(now);
+ break;
+ case NetConnectionStatus.RespondedConnect:
+ SendConnectResponse(now, true);
+ break;
+ default:
+ m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
+ break;
+ }
+ }
}
internal void ExecuteDisconnect(string reason, bool sendByeMessage)
@@ -72,33 +100,48 @@ namespace Lidgren.Network
SendDisconnect(reason, true);
SetStatus(NetConnectionStatus.Disconnected, reason);
+
+ // in case we're still in handshake
+ m_peer.m_handshakes.Remove(m_remoteEndpoint);
+
m_disconnectRequested = false;
m_connectRequested = false;
+ m_handshakeAttempts = 0;
}
- internal void SendConnect()
+ internal void SendConnect(float now)
{
+ m_peer.VerifyNetworkThread();
+
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 1 + 4);
om.m_messageType = NetMessageType.Connect;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
- om.Write((float)NetTime.Now);
+ om.Write(now);
WriteLocalHail(om);
m_peer.SendLibrary(om, m_remoteEndpoint);
- SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
m_connectRequested = false;
+ m_lastHandshakeSendTime = now;
+ m_handshakeAttempts++;
+
+ if (m_handshakeAttempts > 1)
+ m_peer.LogDebug("Resending Connect...");
+ SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
}
- internal void SendConnectResponse(bool onLibraryThread)
+ internal void SendConnectResponse(float now, bool onLibraryThread)
{
+ if (onLibraryThread)
+ m_peer.VerifyNetworkThread();
+
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 1 + 4);
om.m_messageType = NetMessageType.ConnectResponse;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
- om.Write((float)NetTime.Now);
+ om.Write(now);
WriteLocalHail(om);
@@ -107,11 +150,20 @@ namespace Lidgren.Network
else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple(m_remoteEndpoint, om));
+ m_lastHandshakeSendTime = now;
+ m_handshakeAttempts++;
+
+ if (m_handshakeAttempts > 1)
+ m_peer.LogDebug("Resending ConnectResponse...");
+
SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect");
}
internal void SendDisconnect(string reason, bool onLibraryThread)
{
+ if (onLibraryThread)
+ m_peer.VerifyNetworkThread();
+
NetOutgoingMessage om = m_peer.CreateMessage(reason);
om.m_messageType = NetMessageType.Disconnect;
if (onLibraryThread)
@@ -141,6 +193,8 @@ namespace Lidgren.Network
om.Write((float)NetTime.Now);
m_peer.SendLibrary(om, m_remoteEndpoint);
+ m_handshakeAttempts = 0;
+
InitializePing();
if (m_status != NetConnectionStatus.Connected)
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
@@ -152,7 +206,8 @@ namespace Lidgren.Network
public void Approve()
{
m_localHailMessage = null;
- SendConnectResponse(false);
+ m_handshakeAttempts = 0;
+ SendConnectResponse((float)NetTime.Now, false);
}
///
@@ -162,7 +217,8 @@ namespace Lidgren.Network
public void Approve(NetOutgoingMessage localHail)
{
m_localHailMessage = localHail;
- SendConnectResponse(false);
+ m_handshakeAttempts = 0;
+ SendConnectResponse((float)NetTime.Now, false);
}
///
@@ -223,14 +279,14 @@ namespace Lidgren.Network
return;
}
- SendConnectResponse(true);
+ SendConnectResponse((float)now, true);
}
return;
}
if (m_status == NetConnectionStatus.RespondedConnect)
{
// our ConnectResponse must have been lost
- SendConnectResponse(true);
+ SendConnectResponse((float)now, true);
return;
}
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
diff --git a/Lidgren.Network/NetConnection.cs b/Lidgren.Network/NetConnection.cs
index 89c8642..2dab8d0 100644
--- a/Lidgren.Network/NetConnection.cs
+++ b/Lidgren.Network/NetConnection.cs
@@ -90,10 +90,14 @@ namespace Lidgren.Network
if (status == m_status)
return;
+
m_status = status;
if (reason == null)
reason = string.Empty;
+ // new status equals potentially new handshake attempts
+ m_handshakeAttempts = 0;
+
if (m_status == NetConnectionStatus.Connected)
{
m_timeoutDeadline = (float)NetTime.Now + m_peerConfiguration.m_connectionTimeout;
diff --git a/Lidgren.Network/NetPeer.cs b/Lidgren.Network/NetPeer.cs
index bb87e1e..5b523d4 100644
--- a/Lidgren.Network/NetPeer.cs
+++ b/Lidgren.Network/NetPeer.cs
@@ -229,7 +229,7 @@ namespace Lidgren.Network
break;
case NetConnectionStatus.RespondedConnect:
// send another response
- hs.SendConnectResponse(false);
+ hs.SendConnectResponse((float)NetTime.Now, false);
break;
default:
// weird
diff --git a/Lidgren.Network/NetPeerConfiguration.cs b/Lidgren.Network/NetPeerConfiguration.cs
index df07cfb..08affe0 100644
--- a/Lidgren.Network/NetPeerConfiguration.cs
+++ b/Lidgren.Network/NetPeerConfiguration.cs
@@ -44,6 +44,8 @@ namespace Lidgren.Network
internal int m_port;
internal int m_receiveBufferSize;
internal int m_sendBufferSize;
+ internal float m_resendHandshakeInterval;
+ internal int m_maximumHandshakeAttempts;
// bad network simulation
internal float m_loss;
@@ -79,6 +81,8 @@ namespace Lidgren.Network
m_pingInterval = 4.0f;
m_connectionTimeout = 25.0f;
m_useMessageRecycling = true;
+ m_resendHandshakeInterval = 3.0f;
+ m_maximumHandshakeAttempts = 5;
// Maximum transmission unit
// Ethernet can take 1500 bytes of payload, so lets stay below that.
@@ -302,6 +306,24 @@ namespace Lidgren.Network
set { m_acceptIncomingConnections = value; }
}
+ ///
+ /// Gets or sets the number of seconds between handshake attempts
+ ///
+ public float ResendHandshakeInterval
+ {
+ get { return m_resendHandshakeInterval; }
+ set { m_resendHandshakeInterval = value; }
+ }
+
+ ///
+ /// Gets or sets the maximum number of handshake attempts before failing to connect
+ ///
+ public int MaximumHandshakeAttempts
+ {
+ get { return m_maximumHandshakeAttempts; }
+ set { m_maximumHandshakeAttempts = value; }
+ }
+
///
/// Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size
///