介绍开源的.net通信框架NetworkComms框架 源码分析(二十 )ConnectionCreate
原文网址: http://www.cnblogs.com/csdev
Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是:Apache License v2
开源地址是:https://github.com/MarcFletcher/NetworkComms.Net
public abstract partial class Connection { /// <summary> /// Connection information related to this connection. /// 连接信息类 /// </summary> public ConnectionInfo ConnectionInfo { get; protected set; } /// <summary> /// A manual reset event which can be used to handle connection setup and establish. /// 手动类型信号灯 连接创建 Setup /// </summary> protected ManualResetEvent connectionSetupWait = new ManualResetEvent(false); /// <summary> /// A manual reset event which can be used to handle connection setup and establish. /// 手动类型信号灯 连接创建 Establish /// </summary> protected ManualResetEvent connectionEstablishWait = new ManualResetEvent(false); /// <summary> /// A boolean used to signal a connection setup exception. /// 连接创建异常 /// </summary> protected bool connectionSetupException = false; /// <summary> /// If <see cref="connectionSetupException"/> is true provides additional exception information. /// 连接创建异常 字符信息 /// </summary> protected string connectionSetupExceptionStr = ""; /// <summary> /// Create a new connection object /// 创建一个新的连接对象 /// </summary> /// <param name="connectionInfo">连接信息 ConnectionInfo corresponding to the new connection</param> /// <param name="defaultSendReceiveOptions">收发参数 The SendReceiveOptions which should be used as connection defaults</param> protected Connection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions) { //If the application layer protocol is disabled the serialiser must be NullSerializer //and no data processors are allowed. //如果应用层协议禁用 序列化必须使用NullSerializer 并且不能使用处理器 if (connectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled) { if (defaultSendReceiveOptions.Options.ContainsKey("ReceiveConfirmationRequired")) throw new ArgumentException("Attempted to create an unmanaged connection when the provided send receive" + " options specified the ReceiveConfirmationRequired option. Please provide compatible send receive options in order to successfully" + " instantiate this unmanaged connection.", "defaultSendReceiveOptions"); if (defaultSendReceiveOptions.DataSerializer != DPSManager.GetDataSerializer<NullSerializer>()) throw new ArgumentException("Attempted to create an unmanaged connection when the provided send receive" + " options serialiser was not NullSerializer. Please provide compatible send receive options in order to successfully" + " instantiate this unmanaged connection.", "defaultSendReceiveOptions"); ) throw new ArgumentException("Attempted to create an unmanaged connection when the provided send receive" + " options contains data processors. Data processors may not be used with unmanaged connections." + " Please provide compatible send receive options in order to successfully instantiate this unmanaged connection.", "defaultSendReceiveOptions"); } SendTimesMSPerKBCache = new CommsMath(); packetBuilder = new PacketBuilder(); //Initialise the sequence counter using the global value //Subsequent values on this connection are guaranteed to be sequential //初始化数据包顺序号 用的是networkcomms静态类中的值 //顺序号不会重复 packetSequenceCounter = Interlocked.Increment(ref NetworkComms.totalPacketSendCount); ConnectionInfo = connectionInfo; if (defaultSendReceiveOptions != null) ConnectionDefaultSendReceiveOptions = defaultSendReceiveOptions; else ConnectionDefaultSendReceiveOptions = NetworkComms.DefaultSendReceiveOptions; //Add any listener specific packet handlers if required //如果需要添加监听器指定的数据包处理器 if (connectionInfo.ConnectionListener != null) connectionInfo.ConnectionListener.AddListenerPacketHandlersToConnection(this); if (NetworkComms.commsShutdown) throw new ConnectionSetupException("Attempting to create new connection after global NetworkComms.Net 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.LocalEndPoint, connectionInfo.ConnectionType, connectionInfo.ApplicationLayerProtocol)) throw new ConnectionSetupException("A " + connectionInfo.ConnectionType.ToString() + " connection already exists with info " + connectionInfo); //We add a reference in the constructor to ensure any duplicate connection problems are picked up here //添加连接引用到NetworkComms静态类中 NetworkComms.AddConnectionReferenceByRemoteEndPoint(this); } /// <summary> /// Establish this connection /// 创建连接 /// </summary> public void EstablishConnection() { try { bool connectionAlreadyEstablishing = false; lock (_syncRoot) { 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 successfully established."); } else { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Establishing new connection with " + ConnectionInfo); EstablishConnectionSpecific(); if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown) throw new ConnectionSetupException("Connection was closed immediately after handshake. This can occur if a different thread used and subsequently closed this connection."); //Once the above has been done the last step is to allow other threads to use the connection //在连接信息类中,标注连接创建完成 ConnectionInfo.NoteCompleteConnectionEstablish(); //Not all connection types will have a known remote network identifier //所有的连接都有一个网络ID if (ConnectionInfo.NetworkIdentifier != ShortGuid.Empty) NetworkComms.AddConnectionReferenceByIdentifier(this); connectionEstablishWait.Set(); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... connection successfully established with " + ConnectionInfo); } } catch (SocketException e) { //If anything goes wrong we close the connection. //出现错误 关闭连接 CloseConnection(); throw new ConnectionSetupException(e.ToString()); } catch (Exception ex) { //If anything goes wrong we close the connection. //出现错误 关闭连接 CloseConnection(); //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. Should call at least ConnectionHandshake() or TriggerConnectionEstablishDelegates(); /// 创建连接的抽象类 /// </summary> protected abstract void EstablishConnectionSpecific(); /// <summary> /// Performs a connection handshake with the remote end of the connection. /// Exchanges network identifier and any listener whose IPAddress matches the connection localEndPoint IPAddress. /// 连接握手 执行一个连接握手同远程的连接 /// 交换网络ID等 /// /// </summary> protected void ConnectionHandshake() { if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled) throw new CommunicationException("Attempted to perform handshake on connection where the application protocol has been disabled."); //If we are server side and we have just received an incoming connection we need to return a connection identifier //This id will be used in all future connections from this machine //如果我们是服务器端 我们接收到进入的连接并返回一个连接ID给他 //这个ID将用于未来所有的本机连接 if (ConnectionInfo.ServerSide) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Waiting for client connnectionInfo from " + ConnectionInfo); //Wait for the client to send its identification //等待客户端发送它的ID #if NET2 if (!connectionSetupWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS, false)) #else if (!connectionSetupWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS)) #endif throw new ConnectionSetupException("Timeout waiting for client connectionInfo with " + ConnectionInfo + ". Connection created at " + ConnectionInfo.ConnectionCreationTime.ToString("HH:mm:ss.fff") + ", its now " + DateTime.Now.ToString("HH:mm:ss.f")); if (connectionSetupException) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Connection setup exception. ServerSide with " + ConnectionInfo + ", " + connectionSetupExceptionStr); throw new ConnectionSetupException("ServerSide. " + connectionSetupExceptionStr); } //Trigger the connection establish delegates before replying to the connection establish //在回复连接创建之前触发连接创建委托 TriggerConnectionEstablishDelegates(); } else { //If we are client side part of the handshake is to inform the server of a potential local listener //Get a list of existing listeners //如果我们是握手中的客户端 通知服务器他有了一个潜在的本地听众 List<EndPoint> existingLocalListeners = null; if (ConnectionInfo.LocalEndPoint is IPEndPoint) existingLocalListeners = Connection.ExistingLocalListenEndPoints(ConnectionInfo.ConnectionType, )); #if NET4 || NET35 else if (ConnectionInfo.LocalEndPoint is InTheHand.Net.BluetoothEndPoint) existingLocalListeners = Connection.ExistingLocalListenEndPoints(ConnectionInfo.ConnectionType, new InTheHand.Net.BluetoothEndPoint(ConnectionInfo.LocalBTEndPoint.Address, ConnectionInfo.LocalBTEndPoint.Service)); #endif //Check to see if we have a local listener for matching the local endpoint address //If we are client side we use this local listener in our reply to the server //检查我们是否有一个本地的监听器来对应本地的端点地址 //如果我们作为客户端,我们可以使用本地监听器来回复服务器端 EndPoint selectedExistingLocalListenerEndPoint = null; if (existingLocalListeners != null && // If we have a suitable local listener 如果我们有合适的本地监听器 existingLocalListeners.Count > && // If we have a suitable local listener 如果我们有合适的本地监听器 !existingLocalListeners.Contains(ConnectionInfo.RemoteEndPoint)) //If this is not an application loop back connection 如果这不是一个应用的回调连接 selectedExistingLocalListenerEndPoint = (existingLocalListeners.Contains(ConnectionInfo.LocalEndPoint) ? ConnectionInfo.LocalEndPoint : existingLocalListeners[]); //During this exchange we may note an update local listen port //我们可以注意到一个更新的本地监听端口 if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Sending connnectionInfo to " + ConnectionInfo); //Pull-out the parameters we want to send to the server 找出我们想要发送到服务器的参数 //Doing it here rather than all in the following Send Object line keeps it clearer EndPoint selectedLocalListenerEndPoint = (selectedExistingLocalListenerEndPoint != null ? selectedExistingLocalListenerEndPoint : ConnectionInfo.LocalEndPoint); bool connectable = selectedExistingLocalListenerEndPoint != null; //As the client we initiated the connection we now forward our local node identifier to the server //If we are listening we include our local listen port as well //作为客户端我们发送本地ID给服务器 //如果我们监听 我们包含本地监听端口 SendObject(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.ConnectionSetup), new ConnectionInfo(ConnectionInfo.ConnectionType, NetworkComms.NetworkIdentifier, selectedLocalListenerEndPoint, connectable), NetworkComms.InternalFixedSendReceiveOptions); //Wait here for the server end to return its own identifier //在这里等待服务器端返回其自己的标识符 #if NET2 if (!connectionSetupWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS, false)) #else if (!connectionSetupWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS)) #endif throw new ConnectionSetupException("Timeout waiting for server connnectionInfo from " + ConnectionInfo + ". Connection created at " + ConnectionInfo.ConnectionCreationTime.ToString("HH:mm:ss.fff") + ", its now " + DateTime.Now.ToString("HH:mm:ss.f")); //If we are client side we can update the localEndPoint for this connection to reflect what the remote end might see if we are also listening //如果我们是客户端 我们更新本地端点 if (selectedExistingLocalListenerEndPoint != null && selectedExistingLocalListenerEndPoint != ConnectionInfo.LocalEndPoint) { //We should now be able to set the connectionInfo localEndPoint //设置连接信息类的本地端点 NetworkComms.UpdateConnectionReferenceByEndPoint(this, ConnectionInfo.RemoteEndPoint, selectedExistingLocalListenerEndPoint); ConnectionInfo.UpdateLocalEndPointInfo(selectedExistingLocalListenerEndPoint); } if (connectionSetupException) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Connection setup exception. ClientSide with " + ConnectionInfo + ", " + connectionSetupExceptionStr); throw new ConnectionSetupException("ClientSide. " + connectionSetupExceptionStr); } //Trigger the connection establish delegates once the server has replied to the connection establish //如果服务器已经针对连接创建有回复 触发连接创建委托 TriggerConnectionEstablishDelegates(); } } /// <summary> /// Trigger connection establish delegates. /// 触发连接创建委托 /// </summary> protected void TriggerConnectionEstablishDelegates() { //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 /// 如果连接在指定的时间内建设完成 返回True /// </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 successfully 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."); #if NET2 return connectionSetupWait.WaitOne(waitTimeoutMS, false); #else return connectionSetupWait.WaitOne(waitTimeoutMS); #endif } } /// <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 possibleClashWithExistingConnection = 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 possibleClashWithExistingConnection, ref existingConnection); //If we were not successful 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 (possibleClashWithExistingConnection) { //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 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 possibleClashWithExistingConnection, ref existingConnection); //If we still failed then that's it for this establish //尝试依然失败 if (!connectionEstablishedSuccess && !connectionSetupException) { connectionSetupException = true; connectionSetupExceptionStr = "Attempted to establish connection with " + ConnectionInfo + ", but due to an existing connection this was not possible."; } } } //If we are server side and we receive a successful connection setup we can respond to here //如果我们是服务器端并且我们收到一个成功的连接设置,我们可以在此处进行回复 if (connectionEstablishedSuccess && ConnectionInfo.ServerSide) SendObject(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.ConnectionSetup), new ConnectionInfo(ConnectionInfo.ConnectionType, NetworkComms.NetworkIdentifier, ConnectionInfo.LocalEndPoint, true), NetworkComms.InternalFixedSendReceiveOptions); } //Trigger any setup waits //给予信号 触发任何等待设置 connectionSetupWait.Set(); } /// <summary> /// Attempts to complete the connection establish with a minimum of locking to avoid possible deadlocking /// 试图完成连接的创建使用一个最小所来避免可能的冲突 /// </summary> /// <param name="remoteConnectionInfo">远端点连接信息对象 <see cref="ConnectionInfo"/> corresponding with remoteEndPoint</param> /// <param name="possibleClashWithExistingConnection">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 possibleClashWithExistingConnection, ref Connection existingConnection) { lock (NetworkComms.globalDictAndDelegateLocker) { List<Connection> connectionByEndPoint = NetworkComms.GetExistingConnection(ConnectionInfo.RemoteEndPoint, ConnectionInfo.LocalEndPoint, ConnectionInfo.ConnectionType, ConnectionInfo.ApplicationLayerProtocol); //If we no longer have the original endPoint reference (set in the constructor) then the connection must have been closed already //如果我们没有原始端点引用 然后连接必须已经被关闭 ) { connectionSetupException = true; connectionSetupExceptionStr = "Connection setup received after connection closure with " + ConnectionInfo; } else { //COMMENT: As of version 3.0.0 we have allowed loop back connections where the identifier is the same //在3.0版本中 我们允许环回连接标识符是相同的 //We need to check for a possible GUID clash 我们需要检查一个可能的GUID冲突 //Probability of a clash is approx 0.1% if 1E19 connections are maintained simultaneously (This many connections has not be tested ;)) //可能出现冲突 概率为 0.1% 如果同时保持1e19次方 连接 //but hey, we live in a crazy world! 但是,嘿,我们生活在一个疯狂的世界 //if (remoteConnectionInfo.NetworkIdentifier == NetworkComms.NetworkIdentifier) //{ // connectionSetupException = true; // connectionSetupExceptionStr = "Remote peer has same network identifier 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 ] != this) { possibleClashWithExistingConnection = true; existingConnection = connectionByEndPoint[]; } ].ConnectionInfo.NetworkIdentifier != ShortGuid.Empty && connectionByEndPoint[].ConnectionInfo.NetworkIdentifier != remoteConnectionInfo.NetworkIdentifier) { //We are in the same connection, so don't need to throw and exception but the remote network identifier //has changed. //我们在同一个连接,所以不需要抛弃和异常但远程网络标识符已经改变。 //This can happen for connection types where the local connection (this) may not have been closed //这可能发生 由于本地连接没有关闭 //when the remote peer closed. We need to trigger the connection close delegates with the old info, update //the connection info and then call the establish delegates //远程连接关闭时,我们需要触发连接的关闭委托,更新连接信息 然后调用创建委托 #region Reset Connection without closing //Call the connection close delegates try { //Almost there //Last thing is to call any connection specific shutdown delegates if (ConnectionSpecificShutdownDelegate != null) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Triggered connection specific shutdown delegates with " + ConnectionInfo); ConnectionSpecificShutdownDelegate(this); } } catch (Exception ex) { LogTools.LogException(ex, "ConnectionSpecificShutdownDelegateError", "Error while executing connection specific shutdown delegates for " + ConnectionInfo + ". Ensure any shutdown exceptions are caught in your own code."); } try { //Last but not least we call any global connection shutdown delegates if (NetworkComms.globalConnectionShutdownDelegates != null) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Triggered global shutdown delegates with " + ConnectionInfo); NetworkComms.globalConnectionShutdownDelegates(this); } } catch (Exception ex) { LogTools.LogException(ex, "GlobalConnectionShutdownDelegateError", "Error while executing global connection shutdown delegates for " + ConnectionInfo + ". Ensure any shutdown exceptions are caught in your own code."); } EndPoint newRemoteEndPoint; if (this.ConnectionInfo.RemoteEndPoint.GetType() == typeof(IPEndPoint) && remoteConnectionInfo.LocalEndPoint.GetType() == typeof(IPEndPoint)) newRemoteEndPoint = new IPEndPoint(this.ConnectionInfo.RemoteIPEndPoint.Address, remoteConnectionInfo.LocalIPEndPoint.Port); else throw new NotImplementedException("ConnectionSetupHandlerFinal not implemented for EndPoints of type " + this.ConnectionInfo.RemoteEndPoint.GetType()); NetworkComms.UpdateConnectionReferenceByEndPoint(this, newRemoteEndPoint, this.ConnectionInfo.LocalEndPoint); ConnectionInfo.UpdateInfoAfterRemoteHandshake(remoteConnectionInfo, newRemoteEndPoint); //Trigger the establish delegates //出发创建委托 TriggerConnectionEstablishDelegates(); #endregion return true; } else { //Update the connection info 更新连接信息 //We never change the this.ConnectionInfo.RemoteEndPoint.Address as there might be NAT involved //我们不修改ConnnectionInfo.远端点 因为可能有NAT的参与 //We may update the port however 更新端口 EndPoint newRemoteEndPoint; if (this is IPConnection) newRemoteEndPoint = new IPEndPoint(this.ConnectionInfo.RemoteIPEndPoint.Address, remoteConnectionInfo.LocalIPEndPoint.Port); #if NET35 || NET4 else if (this is BluetoothConnection) newRemoteEndPoint = ConnectionInfo.RemoteBTEndPoint; #endif else throw new NotImplementedException("ConnectionSetupHandlerFinal not implemented for EndPoints of type " + this.ConnectionInfo.RemoteEndPoint.GetType()); NetworkComms.UpdateConnectionReferenceByEndPoint(this, newRemoteEndPoint, this.ConnectionInfo.LocalEndPoint); ConnectionInfo.UpdateInfoAfterRemoteHandshake(remoteConnectionInfo, newRemoteEndPoint); return true; } } } return false; } /// <summary> /// Returns ConnectionInfo.ToString /// 返回连接信息类的相关信息 /// </summary> /// <returns></returns> public override string ToString() { return ConnectionInfo.ToString(); } }
介绍开源的.net通信框架NetworkComms框架 源码分析(二十 )ConnectionCreate的更多相关文章
- ABP源码分析二十六:核心框架中的一些其他功能
本文是ABP核心项目源码分析的最后一篇,介绍一些前面遗漏的功能 AbpSession AbpSession: 目前这个和CLR的Session没有什么直接的联系.当然可以自定义的去实现IAbpSess ...
- DotNetty网络通信框架学习之源码分析
DotNetty网络通信框架学习之源码分析 有关DotNetty框架,网上的详细资料不是很多,有不多的几个博友做了简单的介绍,也没有做深入的探究,我也根据源码中提供的demo做一下记录,方便后期查阅. ...
- 框架-springmvc源码分析(二)
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
- 深入理解分布式调度框架TBSchedule及源码分析
简介 由于最近工作比较忙,前前后后花了两个月的时间把TBSchedule的源码翻了个底朝天.关于TBSchedule的使用,网上也有很多参考资料,这里不做过多的阐述.本文着重介绍TBSchedule的 ...
- 设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)
1 智能生活项目需求 看一个具体的需求 1) 我们买了一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作. 2) 这些智能家电来自不同的厂家,我们不想针 ...
- 设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
1 四则运算问题 通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求 1) 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复 2) 在分别输入 a ,b, c, ...
- $Django cbv源码分析 djangorestframework框架之APIView源码分析
1 CBV的源码分析 #视图 class login (View): pass #路由 url(r'^books/$', views.login.as_view()) #阅读源码: #左侧工程栏--- ...
- ④NuPlayer播放框架之Renderer源码分析
[时间:2016-11] [状态:Open] [关键词:android,nuplayer,开源播放器,播放框架,渲染器,render] 0 导读 之前我们分析了NuPlayer的实现代码,本文将重点聚 ...
- ⑤NuPlayer播放框架之GenericSource源码分析
[时间:2017-01] [状态:Open] [关键词:android,nuplayer,开源播放器,播放框架,GenericSource] 0 导读 GenericSource是NuPlayer:: ...
- ③NuPlayer播放框架之类NuPlayer源码分析
[时间:2016-10] [状态:Open] [关键词:android,nuplayer,开源播放器,播放框架] 0 引言 差不多一个月了,继续分析AOSP的播放框架的源码.这次我们需要深入分析的是N ...
随机推荐
- [转]在cocos2d-x中让一个项目适配iphone、iphone retina、ipad、ipad retina四种分辨率
http://cankeyyin.blog.163.com/blog/static/12336178320124149391202/ 原理:将iphone的hd图片给ipad用,即: 使用原iphon ...
- 《OOC》笔记(1)——C语言const、static和extern的用法
<OOC>笔记(1)——C语言const.static和extern的用法 C语言中const关键字用法不少,我只喜欢两种用法.一是用于修饰函数形参,二是用于修饰全局变量和局部变量. 用c ...
- 移动APP的自动化测试
开发移动应用,最耗时耗力的就是手动测试APP的每个功能点或修复bug.有人就会提议App的业务逻辑可以使用nUnit或xUnit测试单元来辅助完成.那用户界面要如何测试?众所周知,移动设备多种多样,数 ...
- Java基础之面向对象以及其他概念
一.基础知识:1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平台,jvm不是跨平台的. JR ...
- [Java面试五]Spring总结以及在面试中的一些问题.
1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控制权,交由Spri ...
- iOS-数据持久化-SQlite3
SQLite3简单介绍 1.ios中数据的存储方式 (1)Plist(NSArray\NSDictionary) (2)Preference(偏好设置\NSUserDefaults) (3)NSCod ...
- CKFinder_AspDotNet_2.4 破解方法 去版权
CKFinder是一个比较好用的Web端文件管理器,虽然UI不是很好看,但是因为能搞到源码,所以比起网上那些只有付费之后才能下载到源码的Web端文件管理器要好许多,至少你可以在确定该控件是否能用在你的 ...
- 解决TryUpdateModel对象为空的问题
MVC中的TryUpdateModel确实给开发带来不少便利,但是如果绑定在View上的文本控件没有填写值的时候,再执行TryUpdateModel对应的实体属性就会为空. 如果数据库中对应的字段不允 ...
- KnockoutJS 3.X API 第四章 表单绑定(9) value绑定
目的 value绑定主要用于DOM元素给视图模型赋值用的.通常用于<input><select><textarea>等元素. value绑定与text绑定的区别在于 ...
- React 入门实例教程
现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...