/// <summary>
/// Adds or updates a peer to the local availability list. Useful for when a peer informs us of an updated availability.
/// </summary>
/// <param name="connectionInfo">The connectionInfo of the remote peer</param>
/// <param name="latestChunkFlags">The new chunk flags</param>
/// <param name="superPeer">True if this peer is a superPeer</param>
/// <param name="setIPEndPointOnline">Set the relevant IPEndPoint online as a result of updating chunk flags</param>
public void AddOrUpdateCachedPeerChunkFlags(ConnectionInfo connectionInfo, ChunkFlags latestChunkFlags, bool superPeer = false, bool setIPEndPointOnline = true)
{
if (connectionInfo.NetworkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty.");
lock (peerLocker)
{
if (connectionInfo.ConnectionType != ConnectionType.TCP) throw new Exception("Only the TCP side of a DFS peer should be tracked.");
//Extract the correct endpoint from the provided connectionInfo
//If this is taken from a connection we are after the remoteEndPoint
IPEndPoint endPointToUse = null;
if (((IPEndPoint)connectionInfo.RemoteEndPoint).Address == IPAddress.Any ||
((IPEndPoint)connectionInfo.RemoteEndPoint).Address == IPAddress.IPv6Any)
endPointToUse = (IPEndPoint)connectionInfo.LocalEndPoint;
else
endPointToUse = (IPEndPoint)connectionInfo.RemoteEndPoint;
string endPointToUseString = endPointToUse.ToString();
//We can only add a peer if it is listening correctly
if (endPointToUse.Port <= DFS.MaxTargetLocalPort && endPointToUse.Port >= DFS.MinTargetLocalPort)
{
//Ensure the endpoint is correctly recorded
RemoveOldPeerAtEndPoint(connectionInfo.NetworkIdentifier, endPointToUse);
//If we have an existing record of this peer
if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(connectionInfo.NetworkIdentifier))
{
//If the existing peerInfo is not aware of this endPoint
if (!peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].PeerContainsIPEndPoint(connectionInfo.NetworkIdentifier, endPointToUse))
{
//Add the information to the peerInfo and local index
peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].AddPeerIPEndPoint(connectionInfo.NetworkIdentifier, endPointToUse);
peerEndPointToNetworkIdentifier[endPointToUseString] = connectionInfo.NetworkIdentifier;
}
//Finally update the chunk flags
peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].PeerChunkFlags.UpdateFlags(latestChunkFlags);
if (DFS.loggingEnabled) DFS.Logger.Trace("Updated existing chunk flags for " + connectionInfo + " [" + latestChunkFlags.NumCompletedChunks() + "].");
}
else
{
//If we don't know anything about this peer we add it to our local swarm availability
//We used comms to get any existing connections to the peer
//We have to create new ConnectionInfo in the select as we need to correctly set the "LOCAL IPEndPoint" when passing to new PeerInfo()
List<ConnectionInfo> peerConnectionInfos = (from current in NetworkComms.GetExistingConnection(connectionInfo.NetworkIdentifier, ConnectionType.TCP) select new ConnectionInfo(ConnectionType.TCP, current.ConnectionInfo.NetworkIdentifier, current.ConnectionInfo.RemoteEndPoint, true)).ToList();
//Don't forget to add the originating info if it was not pulled out from above
ConnectionInfo originatingConnectionInfo = new ConnectionInfo(ConnectionType.TCP, connectionInfo.NetworkIdentifier, endPointToUse, true);
if (!peerConnectionInfos.Contains(originatingConnectionInfo)) peerConnectionInfos.Add(originatingConnectionInfo);
peerAvailabilityByNetworkIdentifierDict.Add(connectionInfo.NetworkIdentifier, new PeerInfo(peerConnectionInfos, latestChunkFlags, superPeer));
//We finish by adding the endPoint references
foreach (ConnectionInfo connInfo in peerConnectionInfos)
peerEndPointToNetworkIdentifier[connInfo.LocalEndPoint.ToString()] = connectionInfo.NetworkIdentifier;
if (DFS.loggingEnabled) DFS.Logger.Trace("Added new chunk flags for " + connectionInfo);
}
if (setIPEndPointOnline)
//By updating cached peer chunk flags we set the peer as being online
peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].SetPeerIPEndPointOnlineStatus(connectionInfo.NetworkIdentifier, endPointToUse, true);
//We will trigger the alive peers event when we have at least a third of the existing peers
if (!alivePeersReceivedEvent.WaitOne(0))
{
int numOnlinePeers = (from current in peerAvailabilityByNetworkIdentifierDict.Values where current.HasAtleastOneOnlineIPEndPoint() select current).Count();
if (numOnlinePeers >= DFS.MaxTotalItemRequests || numOnlinePeers > peerAvailabilityByNetworkIdentifierDict.Count / 3.0)
alivePeersReceivedEvent.Set();
}
}
else
throw new Exception("Attempted to AddOrUpdateCachedPeerChunkFlags for client which was not listening or was using port outside the valid DFS range. Provided connectionInfo: " + connectionInfo + ". EndPointToUse:" + endPointToUse);
//LogTools.LogException("Attempted to AddOrUpdateCachedPeerChunkFlags for client which was not listening or was using port outside the valid DFS range.", "PeerChunkFlagsUpdateError", "IP:" + endPointToUse.Address.ToString() + ", Port:" + endPointToUse.Port);
}
}