networkComms.net2.3.1开源版本,基于gpl V3协议。因为不能公开3.x版本的源码,所以基于此版本进行学习。3.X版本进行了诸多改进和Bug修复,使用方法上两者相差不大。
namespace NetworkCommsDotNet
{
/// <summary>
/// Connection对象 这个类是TcpConnection和 UDPConnnection连接类的父类
/// Connection由以下五个文件组成 大家注意到每个类前面都有个 partial关键字
/// ConnectionCreate.cs <1>
/// ConnectionDelegatesHandlers.cs <2>
/// ConnectionIncomingData.cs <3>
/// ConnectionSendClose.cs <4>
/// ConnectionStatic.cs <5>
/// </summary>
public abstract partial class Connection
{
/// <summary>
/// 当前连接的连接信息类
/// </summary>
public ConnectionInfo ConnectionInfo { get; protected set; }
/// 一个 manualResetEvent信号 用来处理连接的创建 setup
protected ManualResetEvent connectionSetupWait = new ManualResetEvent(false);
/// <summary>
/// 一个 manualResetEvent信号 用来处理连接的创建 establish.
/// </summary>
protected ManualResetEvent connectionEstablishWait = new ManualResetEvent(false);
/// <summary>
/// 连接创建是否异常
/// </summary>
protected bool connectionSetupException = false;
/// <summary>
/// 连接床架异常相关的信息
/// </summary>
protected string connectionSetupExceptionStr = "";
/// <summary>
///创建一个连接对象
/// </summary>
/// <param name="connectionInfo">连接信息类</param>
/// <param name="defaultSendReceiveOptions">默认的收发参数</param>
protected Connection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions)
{
//创建一个方差类 这个是数学模型
SendTimesMSPerKBCache = new CommsMath();
//默认的数据缓冲区大小
dataBuffer = new byte[NetworkComms.ReceiveBufferSizeBytes];
//实例化一个数据包创建器
//PacketBuilder用来保存连接上收到的二进制数据
//他的模型是这样的,连接上收到的第一个字节中的数据,对应的是数据包包头的长度,根据第一个字节中的数据,解析出数据包包头,
//然后根据数据包包头中的信息,信息包括数据包长度,再解析出数据包,然后把数据包交给相应的处理器进行处理
packetBuilder = new PacketBuilder();
//初始化一个顺序号 之后每发送一个数据包顺序后都加1 所以没有包的顺序号都是唯一的
packetSequenceCounter = Interlocked.Increment(ref NetworkComms.totalPacketSendCount);
ConnectionInfo = connectionInfo;
if (defaultSendReceiveOptions != null)
ConnectionDefaultSendReceiveOptions = defaultSendReceiveOptions;
else
//如果没有默认的收发参数 则使用NetworkComms静态类中的默认收发参数
//默认收发参数使用protobuf作为序列化器,没有启用加密和压缩处理器
ConnectionDefaultSendReceiveOptions = NetworkComms.DefaultSendReceiveOptions;
if (NetworkComms.commsShutdown) throw new ConnectionSetupException("Attempting to create new connection after global comms shutdown has been initiated.");
if (ConnectionInfo.ConnectionType == ConnectionType.Undefined || ConnectionInfo.RemoteEndPoint == null)
throw new ConnectionSetupException("ConnectionType and RemoteEndPoint must be defined within provided ConnectionInfo.");
//If a connection already exists with this info then we can throw an exception here to prevent duplicates
if (NetworkComms.ConnectionExists(connectionInfo.RemoteEndPoint, connectionInfo.ConnectionType))
throw new ConnectionSetupException("A connection already exists with " + ConnectionInfo);
//添加连接到NetworkComms静态类中
//比如说服务器的话,会把监听到的连接都添加一个引用到NetworkComms静态类中
//如果某个连接断开了,networkComms静态类中,也会相应的删除这个连接。
NetworkComms.AddConnectionByReferenceEndPoint(this);
}
/// <summary>
///创建这个连接
/// </summary>
public void EstablishConnection()
{
try
{
bool connectionAlreadyEstablishing = false;
lock (delegateLocker)
{
if (ConnectionInfo.ConnectionState == ConnectionState.Established) return;
else if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown) throw new ConnectionSetupException("Attempting to re-establish a closed connection. Please create a new connection instead.");
else if (ConnectionInfo.ConnectionState == ConnectionState.Establishing)
connectionAlreadyEstablishing = true;
else
ConnectionInfo.NoteStartConnectionEstablish();
}
if (connectionAlreadyEstablishing)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Waiting for connection with " + ConnectionInfo + " to be established.");
if (!WaitForConnectionEstablish(NetworkComms.ConnectionEstablishTimeoutMS))
throw new ConnectionSetupException("Timeout waiting for connection to be succesfully established.");
}
else
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Establishing new connection with " + ConnectionInfo);
//这是个virtual方法,具体工作交给子类去做,TCPConnection类负责建立Tcp连接 UDPConnection类负责建UDP连接
EstablishConnectionSpecific();
if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown) throw new ConnectionSetupException("Connection was closed during establish handshake.");
if (ConnectionInfo.NetworkIdentifier == ShortGuid.Empty)
throw new ConnectionSetupException("Remote network identifier should have been set by this point.");
//Once the above has been done the last step is to allow other threads to use the connection
ConnectionInfo.NoteCompleteConnectionEstablish();
NetworkComms.AddConnectionReferenceByIdentifier(this);
connectionEstablishWait.Set();
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... connection succesfully established with " + ConnectionInfo);
}
}
catch (SocketException e)
{
//If anything goes wrong we close the connection.
CloseConnection(true, 43);
throw new ConnectionSetupException(e.ToString());
}
catch (Exception ex)
{
//If anything goes wrong we close the connection.
CloseConnection(true, 44);
//For some odd reason not all SocketExceptions get caught above, so another check here
if (ex.GetBaseException().GetType() == typeof(SocketException))
throw new ConnectionSetupException(ex.ToString());
else
throw;
}
}
/// <summary>
/// Any connection type specific establish tasks. Base should be called to trigger connection establish delegates
/// </summary>
protected virtual void EstablishConnectionSpecific()
{
//Call asynchronous connection establish delegates here
if (NetworkComms.globalConnectionEstablishDelegatesAsync != null)
{
NetworkComms.CommsThreadPool.EnqueueItem(QueueItemPriority.Normal, new WaitCallback((obj) =>
{
Connection connectionParam = obj as Connection;
NetworkComms.globalConnectionEstablishDelegatesAsync(connectionParam);
}), this);
}
//Call synchronous connection establish delegates here
if (NetworkComms.globalConnectionEstablishDelegatesSync != null)
NetworkComms.globalConnectionEstablishDelegatesSync(this);
}
/// <summary>
/// Return true if the connection is established within the provided timeout, otherwise false
/// </summary>
/// <param name="waitTimeoutMS">Wait time in milliseconds before returning</param>
/// <returns>True if the wait was triggered, false otherwise after the provided timeout.</returns>
protected bool WaitForConnectionEstablish(int waitTimeoutMS)
{
if (ConnectionInfo.ConnectionState == ConnectionState.Established)
return true;
else
{
if (NetworkComms.LoggingEnabled)
NetworkComms.Logger.Trace("Waiting for new connection to be succesfully established before continuing with " + ConnectionInfo);
if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown)
throw new ConnectionShutdownException("Attempted to wait for connection establish on a connection that is already shutdown.");
return connectionSetupWait.WaitOne(waitTimeoutMS);
}
}
/// <summary>
/// Handle an incoming ConnectionSetup packet type
/// </summary>
/// <param name="packetDataSection">Serialised handshake data</param>
internal void ConnectionSetupHandler(MemoryStream packetDataSection)
{
//We should never be trying to handshake an established connection
ConnectionInfo remoteConnectionInfo = NetworkComms.InternalFixedSendReceiveOptions.DataSerializer.DeserialiseDataObject<ConnectionInfo>(packetDataSection,
NetworkComms.InternalFixedSendReceiveOptions.DataProcessors, NetworkComms.InternalFixedSendReceiveOptions.Options);
if (ConnectionInfo.ConnectionType != remoteConnectionInfo.ConnectionType)
{
connectionSetupException = true;
connectionSetupExceptionStr = "Remote connectionInfo provided connectionType did not match expected connection type.";
}
else
{
//We use the following bool to track a possible existing connection which needs closing
bool possibleClashConnectionWithPeer_ByEndPoint = false;
Connection existingConnection = null;
//We first try to establish everything within this lock in one go
//If we can't quite complete the establish we have to come out of the lock at try to sort the problem
bool connectionEstablishedSuccess = ConnectionSetupHandlerFinal(remoteConnectionInfo, ref possibleClashConnectionWithPeer_ByEndPoint, ref existingConnection);
//If we were not succesfull at establishing the connection we need to sort it out!
if (!connectionEstablishedSuccess && !connectionSetupException)
{
if (existingConnection == null) throw new Exception("Connection establish issues and existingConnection was left as null.");
if (possibleClashConnectionWithPeer_ByEndPoint)
{
//If we have a clash by endPoint we test the existing connection
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Existing connection with " + ConnectionInfo + ". Testing existing connection.");
if (existingConnection.ConnectionAlive(1000))
{
//If the existing connection comes back as alive we don't allow this one to go any further
//This might happen if two peers try to connect to each other at the same time
connectionSetupException = true;
connectionSetupExceptionStr = " ... existing live connection at provided end point for this connection (" + ConnectionInfo + "), there should be no need for a second.";
}
}
//We only try again if we did not log an exception
if (!connectionSetupException)
{
//Once we have tried to sort the problem we can try to finish the establish one last time
connectionEstablishedSuccess = ConnectionSetupHandlerFinal(remoteConnectionInfo, ref possibleClashConnectionWithPeer_ByEndPoint, ref existingConnection);
//If we still failed then that's it for this establish
if (!connectionEstablishedSuccess && !connectionSetupException)
{
connectionSetupException = true;
connectionSetupExceptionStr = "Attempted to establish conneciton with " + ConnectionInfo + ", but due to an existing connection this was not possible.";
}
}
}
}
//Trigger any setup waits
connectionSetupWait.Set();
}
/// <summary>
/// Attempts to complete the connection establish with a minimum of locking to prevent possible deadlocking
/// </summary>
/// <param name="remoteConnectionInfo"><see cref="ConnectionInfo"/> corresponding with remoteEndPoint</param>
/// <param name="possibleClashConnectionWithPeer_ByEndPoint">True if a connection already exists with provided remoteEndPoint</param>
/// <param name="existingConnection">A reference to an existing connection if it exists</param>
/// <returns>True if connection is successfully setup, otherwise false</returns>
private bool ConnectionSetupHandlerFinal(ConnectionInfo remoteConnectionInfo, ref bool possibleClashConnectionWithPeer_ByEndPoint, ref Connection existingConnection)
{
lock (NetworkComms.globalDictAndDelegateLocker)
{
Connection connectionByEndPoint = NetworkComms.GetExistingConnection(ConnectionInfo.RemoteEndPoint, ConnectionInfo.ConnectionType);
//If we no longer have the original endPoint reference (set in the constructor) then the connection must have been closed already
if (connectionByEndPoint == null)
{
connectionSetupException = true;
connectionSetupExceptionStr = "Connection setup received after connection closure with " + ConnectionInfo;
}
else
{
//We need to check for a possible GUID clash
//Probability of a clash is approx 0.1% if 1E19 connection are maintained simultaneously (This many connections has not be tested ;))
//but hey, we live in a crazy world!
if (remoteConnectionInfo.NetworkIdentifier == NetworkComms.NetworkIdentifier)
{
connectionSetupException = true;
connectionSetupExceptionStr = "Remote peer has same network idendifier to local, " + remoteConnectionInfo.NetworkIdentifier + ". A real duplication is vanishingly improbable so this exception has probably been thrown because the local and remote application are the same.";
}
else if (connectionByEndPoint != this)
{
possibleClashConnectionWithPeer_ByEndPoint = true;
existingConnection = connectionByEndPoint;
}
else
{
//Update the connection info
//We never change the this.ConnectionInfo.RemoteEndPoint.Address as there might be NAT involved
//We may update the port however
IPEndPoint newRemoteIPEndPoint = new IPEndPoint(this.ConnectionInfo.RemoteEndPoint.Address, remoteConnectionInfo.LocalEndPoint.Port);
NetworkComms.UpdateConnectionReferenceByEndPoint(this, newRemoteIPEndPoint);
ConnectionInfo.UpdateInfoAfterRemoteHandshake(remoteConnectionInfo, newRemoteIPEndPoint);
return true;
}
}
}
return false;
}
/// <summary>
/// Returns ConnectionInfo.ToString
/// </summary>
/// <returns></returns>
public override string ToString()
{
return ConnectionInfo.ToString();
}
}
}
来自英国剑桥的c#网络通讯框架 开源版本: networkcomms2.3.1 可以进入此页面下载 networkcomms网络通讯框架学习
【开源下载】基于TCP网络通信的即时聊天系统(IM系统)(c#源码)
[源码下载]Demo2.模拟简单登陆-效果图 基于networkcomms2.3.1
[源码下载]Demo1 客户端从服务器获取信息(基于networkcomms2.3.1)
【开源下载】基于TCP网络通信的自动升级程序c#源码