e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
545 lines
23 KiB
C#
545 lines
23 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="PeerEndPoint.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
namespace System.Net.PeerToPeer.Collaboration
|
|
{
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Text;
|
|
using System.Runtime.InteropServices;
|
|
using System.Net.Sockets;
|
|
using System.ComponentModel;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.Serialization;
|
|
using System.Security.Permissions;
|
|
|
|
/// <summary>
|
|
/// This is the event args class we give back when
|
|
/// we have have a name changed event fired by native
|
|
/// </summary>
|
|
public class NameChangedEventArgs : EventArgs
|
|
{
|
|
private PeerEndPoint m_peerEndPoint;
|
|
private PeerContact m_peerContact;
|
|
private string m_name;
|
|
|
|
internal NameChangedEventArgs(PeerEndPoint peerEndPoint, PeerContact peerContact,
|
|
string name)
|
|
{
|
|
m_peerEndPoint = peerEndPoint;
|
|
m_peerContact = peerContact;
|
|
m_name = name;
|
|
}
|
|
|
|
public PeerEndPoint PeerEndPoint
|
|
{
|
|
get{
|
|
return m_peerEndPoint;
|
|
}
|
|
}
|
|
|
|
public PeerContact PeerContact
|
|
{
|
|
get{
|
|
return m_peerContact;
|
|
}
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get{
|
|
return m_name;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// PeerEndpoint class encapsulates the functionality of an
|
|
/// endpoint in the peer collaboration scope.
|
|
/// </summary>
|
|
[Serializable]
|
|
public class PeerEndPoint : IDisposable, IEquatable<PeerEndPoint>, ISerializable
|
|
{
|
|
private string m_endPointName;
|
|
private IPEndPoint m_endPoint;
|
|
private ISynchronizeInvoke m_synchronizingObject;
|
|
|
|
public PeerEndPoint() { }
|
|
|
|
public PeerEndPoint(IPEndPoint endPoint):this(endPoint, null)
|
|
{ }
|
|
|
|
public PeerEndPoint(IPEndPoint endPoint, string endPointName)
|
|
{
|
|
if (endPoint == null){
|
|
throw new ArgumentNullException("endPoint");
|
|
}
|
|
|
|
//
|
|
// Validate that this is an IPv6 address
|
|
//
|
|
if ((m_endPoint != null) && (m_endPoint.AddressFamily != AddressFamily.InterNetworkV6)){
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerEndPoint Endpoint set parameter is not IPv6.");
|
|
throw new ArgumentException("endPoint", SR.GetString(SR.Collab_EndPointNotIPv6Error));
|
|
}
|
|
|
|
m_endPoint = endPoint;
|
|
m_endPointName = endPointName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor to enable serialization
|
|
/// </summary>
|
|
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
|
|
protected PeerEndPoint(SerializationInfo serializationInfo, StreamingContext streamingContext)
|
|
{
|
|
m_endPointName = serializationInfo.GetString("_EndPointName");
|
|
m_endPoint = (IPEndPoint)serializationInfo.GetValue("_EndPoint", typeof(IPEndPoint));
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get { return m_endPointName; }
|
|
set { m_endPointName = value; }
|
|
}
|
|
|
|
public IPEndPoint EndPoint
|
|
{
|
|
get { return m_endPoint; }
|
|
set {
|
|
//
|
|
// Validate that this is an IPv6 address
|
|
//
|
|
if ((m_endPoint != null) && (m_endPoint.AddressFamily != AddressFamily.InterNetworkV6)){
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerEndPoint Endpoint set parameter is not IPv6.");
|
|
throw new PeerToPeerException(SR.GetString(SR.Collab_EndPointNotIPv6Error));
|
|
}
|
|
m_endPoint = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets and set the object used to marshall event handlers calls for stand alone
|
|
/// events
|
|
/// </summary>
|
|
[Browsable(false), DefaultValue(null), Description(SR.SynchronizingObject)]
|
|
public ISynchronizeInvoke SynchronizingObject
|
|
{
|
|
get
|
|
{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
return m_synchronizingObject;
|
|
}
|
|
set
|
|
{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
m_synchronizingObject = value;
|
|
}
|
|
}
|
|
|
|
private event EventHandler<NameChangedEventArgs> m_nameChanged;
|
|
public event EventHandler<NameChangedEventArgs> NameChanged
|
|
{
|
|
// <SecurityKernel Critical="True" Ring="1">
|
|
// <ReferencesCritical Name="Method: CollaborationHelperFunctions.Initialize():System.Void" Ring="1" />
|
|
// <ReferencesCritical Name="Method: AddNameChanged(EventHandler`1<System.Net.PeerToPeer.Collaboration.NameChangedEventArgs>):Void" Ring="1" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
add{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
|
|
CollaborationHelperFunctions.Initialize();
|
|
|
|
PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
|
|
|
|
AddNameChanged(value);
|
|
}
|
|
// <SecurityKernel Critical="True" Ring="1">
|
|
// <ReferencesCritical Name="Method: CollaborationHelperFunctions.Initialize():System.Void" Ring="1" />
|
|
// <ReferencesCritical Name="Method: RemoveNameChanged(EventHandler`1<System.Net.PeerToPeer.Collaboration.NameChangedEventArgs>):Void" Ring="2" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
remove{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
|
|
CollaborationHelperFunctions.Initialize();
|
|
|
|
PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
|
|
|
|
RemoveNameChanged(value);
|
|
}
|
|
}
|
|
|
|
#region Name changed event variables
|
|
private object m_lockNameChangedEvent;
|
|
private object LockNameChangedEvent
|
|
{
|
|
get{
|
|
if (m_lockNameChangedEvent == null){
|
|
object o = new object();
|
|
Interlocked.CompareExchange(ref m_lockNameChangedEvent, o, null);
|
|
}
|
|
return m_lockNameChangedEvent;
|
|
}
|
|
}
|
|
private RegisteredWaitHandle m_regNameChangedWaitHandle;
|
|
private AutoResetEvent m_nameChangedEvent;
|
|
private SafeCollabEvent m_safeNameChangedEvent;
|
|
#endregion
|
|
|
|
// <SecurityKernel Critical="True" Ring="0">
|
|
// <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabRegisterEvent(Microsoft.Win32.SafeHandles.SafeWaitHandle,System.UInt32,System.Net.PeerToPeer.Collaboration.PEER_COLLAB_EVENT_REGISTRATION&,System.Net.PeerToPeer.Collaboration.SafeCollabEvent&):System.Int32" />
|
|
// <SatisfiesLinkDemand Name="WaitHandle.get_SafeWaitHandle():Microsoft.Win32.SafeHandles.SafeWaitHandle" />
|
|
// <ReferencesCritical Name="Method: NameChangedCallback(Object, Boolean):Void" Ring="1" />
|
|
// <ReferencesCritical Name="Field: m_safeNameChangedEvent" Ring="1" />
|
|
// <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
private void AddNameChanged(EventHandler<NameChangedEventArgs> callback)
|
|
{
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering AddNameChanged().");
|
|
|
|
//
|
|
// Register a wait handle if one has not been registered already
|
|
//
|
|
lock (LockNameChangedEvent){
|
|
if (m_nameChanged == null){
|
|
|
|
m_nameChangedEvent = new AutoResetEvent(false);
|
|
|
|
//
|
|
// Register callback with a wait handle
|
|
//
|
|
|
|
m_regNameChangedWaitHandle = ThreadPool.RegisterWaitForSingleObject(m_nameChangedEvent, //Event that triggers the callback
|
|
new WaitOrTimerCallback(NameChangedCallback), //callback to be called
|
|
null, //state to be passed
|
|
-1, //Timeout - aplicable only for timers
|
|
false //call us everytime the event is set
|
|
);
|
|
PEER_COLLAB_EVENT_REGISTRATION pcer = new PEER_COLLAB_EVENT_REGISTRATION();
|
|
|
|
pcer.eventType = PeerCollabEventType.EndPointChanged;
|
|
pcer.pInstance = IntPtr.Zero;
|
|
|
|
//
|
|
// Register event with collab
|
|
//
|
|
|
|
int errorCode = UnsafeCollabNativeMethods.PeerCollabRegisterEvent(
|
|
m_nameChangedEvent.SafeWaitHandle,
|
|
1,
|
|
ref pcer,
|
|
out m_safeNameChangedEvent);
|
|
if (errorCode != 0){
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabRegisterEvent returned with errorcode {0}", errorCode);
|
|
throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_NameChangedRegFailed), errorCode);
|
|
}
|
|
}
|
|
m_nameChanged += callback;
|
|
}
|
|
|
|
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "AddNameChanged() successful.");
|
|
}
|
|
|
|
// <SecurityKernel Critical="True" Ring="1">
|
|
// <ReferencesCritical Name="Field: m_safeNameChangedEvent" Ring="1" />
|
|
// <ReferencesCritical Name="Method: CollaborationHelperFunctions.CleanEventVars(System.Threading.RegisteredWaitHandle&,System.Net.PeerToPeer.Collaboration.SafeCollabEvent&,System.Threading.AutoResetEvent&):System.Void" Ring="1" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
private void RemoveNameChanged(EventHandler<NameChangedEventArgs> callback)
|
|
{
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "RemoveNameChanged() called.");
|
|
lock (LockNameChangedEvent){
|
|
m_nameChanged -= callback;
|
|
if (m_nameChanged == null){
|
|
CollaborationHelperFunctions.CleanEventVars(ref m_regNameChangedWaitHandle,
|
|
ref m_safeNameChangedEvent,
|
|
ref m_nameChangedEvent);
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Clean NameChanged variables successful.");
|
|
}
|
|
}
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "RemoveNameChanged() successful.");
|
|
}
|
|
|
|
// <SecurityKernel Critical="True" Ring="0">
|
|
// <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabGetEventData(System.Net.PeerToPeer.Collaboration.SafeCollabEvent,System.Net.PeerToPeer.Collaboration.SafeCollabData&):System.Int32" />
|
|
// <SatisfiesLinkDemand Name="SafeHandle.get_IsInvalid():System.Boolean" />
|
|
// <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
|
|
// <SatisfiesLinkDemand Name="Marshal.PtrToStructure(System.IntPtr,System.Type):System.Object" />
|
|
// <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
|
|
// <ReferencesCritical Name="Local eventData of type: SafeCollabData" Ring="1" />
|
|
// <ReferencesCritical Name="Field: m_safeNameChangedEvent" Ring="1" />
|
|
// <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
|
|
// <ReferencesCritical Name="Method: CollaborationHelperFunctions.ConvertPEER_ENDPOINTToPeerEndPoint(System.Net.PeerToPeer.Collaboration.PEER_ENDPOINT):System.Net.PeerToPeer.Collaboration.PeerEndPoint" Ring="1" />
|
|
// <ReferencesCritical Name="Method: CollaborationHelperFunctions.ConvertPEER_CONTACTToPeerContact(System.Net.PeerToPeer.Collaboration.PEER_CONTACT):System.Net.PeerToPeer.Collaboration.PeerContact" Ring="2" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
private void NameChangedCallback(object state, bool timedOut)
|
|
{
|
|
SafeCollabData eventData = null;
|
|
int errorCode = 0;
|
|
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "NameChangedCallback() called.");
|
|
|
|
if (m_Disposed) return;
|
|
|
|
while (true){
|
|
NameChangedEventArgs nameChangedArgs = null;
|
|
|
|
//
|
|
// Get the event data for the fired event
|
|
//
|
|
try{
|
|
lock (LockNameChangedEvent)
|
|
{
|
|
if (m_safeNameChangedEvent.IsInvalid) return;
|
|
errorCode = UnsafeCollabNativeMethods.PeerCollabGetEventData(m_safeNameChangedEvent,
|
|
out eventData);
|
|
}
|
|
|
|
if (errorCode == UnsafeCollabReturnCodes.PEER_S_NO_EVENT_DATA)
|
|
break;
|
|
else if (errorCode != 0){
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabGetEventData returned with errorcode {0}", errorCode);
|
|
throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetNameChangedDataFailed), errorCode);
|
|
}
|
|
|
|
PEER_COLLAB_EVENT_DATA ped = (PEER_COLLAB_EVENT_DATA)Marshal.PtrToStructure(eventData.DangerousGetHandle(),
|
|
typeof(PEER_COLLAB_EVENT_DATA));
|
|
if (ped.eventType == PeerCollabEventType.EndPointChanged){
|
|
PEER_EVENT_ENDPOINT_CHANGED_DATA epData = ped.endpointChangedData;
|
|
PeerEndPoint peerEndPoint = null;
|
|
|
|
if (epData.pEndPoint != IntPtr.Zero){
|
|
PEER_ENDPOINT pe = (PEER_ENDPOINT)Marshal.PtrToStructure(epData.pEndPoint, typeof(PEER_ENDPOINT));
|
|
peerEndPoint = CollaborationHelperFunctions.ConvertPEER_ENDPOINTToPeerEndPoint(pe);
|
|
}
|
|
|
|
if ((peerEndPoint != null) && Equals(peerEndPoint)){
|
|
PeerContact peerContact = null;
|
|
|
|
if (epData.pContact != IntPtr.Zero){
|
|
PEER_CONTACT pc = (PEER_CONTACT)Marshal.PtrToStructure(epData.pContact, typeof(PEER_CONTACT));
|
|
peerContact = CollaborationHelperFunctions.ConvertPEER_CONTACTToPeerContact(pc);
|
|
}
|
|
|
|
|
|
nameChangedArgs = new NameChangedEventArgs(peerEndPoint,
|
|
peerContact,
|
|
peerEndPoint.Name);
|
|
}
|
|
}
|
|
}
|
|
finally{
|
|
if (eventData != null) eventData.Dispose();
|
|
}
|
|
|
|
//
|
|
// Fire the callback with the marshalled event args data
|
|
//
|
|
|
|
if (nameChangedArgs != null){
|
|
OnNameChanged(nameChangedArgs);
|
|
|
|
//
|
|
// Change the name with the new name
|
|
//
|
|
Name = nameChangedArgs.PeerEndPoint.Name;
|
|
}
|
|
}
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving NameChangedCallback().");
|
|
}
|
|
|
|
protected void OnNameChanged(NameChangedEventArgs nameChangedArgs)
|
|
{
|
|
EventHandler<NameChangedEventArgs> handlerCopy = m_nameChanged;
|
|
|
|
if (handlerCopy != null){
|
|
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
|
|
SynchronizingObject.BeginInvoke(handlerCopy, new object[] { this, nameChangedArgs });
|
|
else
|
|
handlerCopy(this, nameChangedArgs);
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Fired the name changed event callback.");
|
|
}
|
|
}
|
|
|
|
public bool Equals(PeerEndPoint other)
|
|
{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
|
|
//
|
|
// Equality means same ipendpoints
|
|
//
|
|
|
|
if (other != null){
|
|
return other.EndPoint.Equals(EndPoint);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
PeerEndPoint comparandPeerEndPoint = obj as PeerEndPoint;
|
|
if (comparandPeerEndPoint != null){
|
|
return comparandPeerEndPoint.EndPoint.Equals(EndPoint);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public new static bool Equals(object objA, object objB)
|
|
{
|
|
PeerEndPoint comparandPeerEndPoint1 = objA as PeerEndPoint;
|
|
PeerEndPoint comparandPeerEndPoint2 = objB as PeerEndPoint;
|
|
|
|
if ((comparandPeerEndPoint1 != null) && (comparandPeerEndPoint2 != null)){
|
|
return comparandPeerEndPoint1.EndPoint.Equals(comparandPeerEndPoint2.EndPoint);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
|
|
return EndPoint.GetHashCode();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
|
|
|
|
return m_endPointName;
|
|
}
|
|
|
|
private bool m_Disposed;
|
|
|
|
// <SecurityKernel Critical="True" Ring="2">
|
|
// <ReferencesCritical Name="Method: Dispose(Boolean):Void" Ring="2" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
// <SecurityKernel Critical="True" Ring="1">
|
|
// <ReferencesCritical Name="Field: m_safeNameChangedEvent" Ring="1" />
|
|
// <ReferencesCritical Name="Method: CollaborationHelperFunctions.CleanEventVars(System.Threading.RegisteredWaitHandle&,System.Net.PeerToPeer.Collaboration.SafeCollabEvent&,System.Threading.AutoResetEvent&):System.Void" Ring="1" />
|
|
// </SecurityKernel>
|
|
[System.Security.SecurityCritical]
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!m_Disposed){
|
|
CollaborationHelperFunctions.CleanEventVars(ref m_regNameChangedWaitHandle,
|
|
ref m_safeNameChangedEvent,
|
|
ref m_nameChangedEvent);
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Clean NameChanged variables successful.");
|
|
}
|
|
m_Disposed = true;
|
|
}
|
|
|
|
// <SecurityKernel Critical="True" Ring="0">
|
|
// <SatisfiesLinkDemand Name="GetObjectData(SerializationInfo, StreamingContext):Void" />
|
|
// </SecurityKernel>
|
|
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.Net.dll is still using pre-v4 security model and needs this demand")]
|
|
[System.Security.SecurityCritical]
|
|
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)]
|
|
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
GetObjectData(info, context);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is made virtual so that derived types can be implemented correctly
|
|
/// </summary>
|
|
[SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
|
|
protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
info.AddValue("_EndPointName", m_endPointName);
|
|
info.AddValue("_EndPoint", m_endPoint);
|
|
}
|
|
|
|
internal void TracePeerEndPoint()
|
|
{
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Contents of the PeerEndPoint");
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "\tEndPoint: {0}", (EndPoint != null? EndPoint.ToString(): null));
|
|
Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "\tDescription: {0}", Name);
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a collection of PeerEndPoints
|
|
/// </summary>
|
|
[Serializable]
|
|
public class PeerEndPointCollection : Collection<PeerEndPoint>, IEquatable<PeerEndPointCollection>
|
|
{
|
|
internal PeerEndPointCollection() { }
|
|
|
|
protected override void SetItem(int index, PeerEndPoint item)
|
|
{
|
|
//
|
|
// Null peerendpoints not allowed
|
|
//
|
|
if (item == null){
|
|
throw new ArgumentNullException("item");
|
|
}
|
|
base.SetItem(index, item);
|
|
}
|
|
|
|
protected override void InsertItem(int index, PeerEndPoint item)
|
|
{
|
|
//
|
|
// Null peerendpoints not allowed
|
|
//
|
|
if (item == null){
|
|
throw new ArgumentNullException("item");
|
|
}
|
|
base.InsertItem(index, item);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
bool first = true;
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
foreach (PeerEndPoint peerEndPoint in this){
|
|
if (!first){
|
|
builder.Append(", ");
|
|
}
|
|
else{
|
|
first = false;
|
|
}
|
|
builder.Append(peerEndPoint.ToString());
|
|
}
|
|
return builder.ToString();
|
|
}
|
|
|
|
public bool Equals(PeerEndPointCollection other)
|
|
{
|
|
bool equal = false;
|
|
|
|
if (other != null){
|
|
foreach (PeerEndPoint peerEndPoint1 in other)
|
|
foreach (PeerEndPoint peerEndPoint2 in this)
|
|
if (!peerEndPoint1.Equals(peerEndPoint2)){
|
|
return equal;
|
|
}
|
|
equal = true;
|
|
}
|
|
|
|
return equal;
|
|
}
|
|
}
|
|
|
|
}
|