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#源码
时间: 2024-10-07 02:22:51