介绍开源的.net通信框架NetworkComms框架 源码分析(十七 ) ConnectionSendClose
原文网址: http://www.cnblogs.com/csdev
Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是:Apache License v2
开源地址是:https://github.com/MarcFletcher/NetworkComms.Net
public abstract partial class Connection : IDisposable { /// <summary> /// Lockers for maintaining thread safe operation /// 同步锁 /// </summary> protected object sendLocker = new object(); /// <summary> /// A NetworkComms.Net math object for tracking send times. Used to prevent send deadlocks. /// Initialised at 1000 milliseconds per KB write speed, corresponding with 1KB / second. /// 一个数学类 用来跟踪发送事件 以阻止死锁显现的发生 /// 初始化为 1KB每秒的发送速度 /// </summary> protected CommsMath SendTimesMSPerKBCache; /// <summary> /// A counter which is incremented during every a send. The current value is included in the header of all sent packets. /// 数据包的顺序号 一个递增计数器,在每一个发送数据包的报头中会包含。 /// </summary> protected long packetSequenceCounter; /// <summary> /// Maintains a list of sent packets for the purpose of confirmation and possible resends. /// 同步锁 /// </summary> object sentPacketsLocker = new object(); //已发送过的数据包存放在此处 以备对方没有收到需要重发 超过一定的时间会删除 只保留一定时间内的数据包 Dictionary<string, SentPacket> sentPackets = new Dictionary<string, SentPacket>(); /// <summary> /// Send bytes on an unmanaged connection /// 在“未托管”的连接上发送字节 /// </summary> /// <param name="bytesToSend">The bytes to send</param> public void SendUnmanagedBytes(byte[] bytesToSend) { if (ConnectionInfo.ApplicationLayerProtocol != ApplicationLayerProtocolStatus.Disabled) throw new CommunicationException("Attempted to send unmanaged bytes on a managed connection. This method should only be used on connections which have the ApplicationLayerProtocol disabled."); SendObject<byte[]>(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged), bytesToSend); } /// <summary> /// Send an object using the connection default <see cref="SendReceiveOptions"/> /// 发送数据使用默认的收发参数 /// </summary> /// <param name="sendingPacketType">消息类型 The sending packet type</param> /// <param name="objectToSend">发送的对象 The object to send</param> public void SendObject<sendObjectType>(string sendingPacketType, sendObjectType objectToSend) { SendObject(sendingPacketType, objectToSend, ConnectionDefaultSendReceiveOptions); } /// <summary> /// Send an object using the connection default <see cref="SendReceiveOptions"/> /// 发送数据使用默认的收发参数 /// </summary> /// <param name="sendingPacketType">消息类型 The sending packet type</param> /// <param name="objectToSend">发送的对象 The object to send</param> /// <param name="packetSequenceNumber">发送的数据包的顺序号 The sequence number of the packet sent</param> public void SendObject<sendObjectType>(string sendingPacketType, sendObjectType objectToSend, out long packetSequenceNumber) { SendObject(sendingPacketType, objectToSend, ConnectionDefaultSendReceiveOptions, out packetSequenceNumber); } /// <summary> /// Send an object using the provided SendReceiveOptions /// 用参数中指定的收发参数 发送数据包 /// </summary> /// <param name="sendingPacketType">消息类型 The packet type to use for send</param> /// <param name="objectToSend">发送的对象 The object to send</param> /// <param name="options">收发参数 Send specific <see cref="SendReceiveOptions"/></param> public void SendObject<sendObjectType>(string sendingPacketType, sendObjectType objectToSend, SendReceiveOptions options) { //Check to see if we already have a packet //检查要发送的数据本身是否为数据包 Packet objectToSendAsPacket = objectToSend as Packet; if (objectToSendAsPacket == null) { using (Packet sendPacket = new Packet(sendingPacketType, objectToSend, options)) SendPacket<sendObjectType>(sendPacket); } else { if (objectToSendAsPacket.PacketHeader.PacketType != sendingPacketType) throw new ArgumentException("Unable to send object of type Packet if the PacketHeader.PacketType and sendingPacketType do not match."); SendPacket<sendObjectType>(objectToSendAsPacket); } } /// <summary> /// Send an object using the provided SendReceiveOptions /// 用参数中指定的收发参数 发送数据包 /// </summary> /// <param name="sendingPacketType">消息类型 The packet type to use for send</param> /// <param name="objectToSend">发送的对象 The object to send</param> /// <param name="options">收发参数 Send specific <see cref="SendReceiveOptions"/></param> /// <param name="packetSequenceNumber">顺序号 The sequence number of the packet sent</param> public void SendObject<sendObjectType>(string sendingPacketType, sendObjectType objectToSend, SendReceiveOptions options, out long packetSequenceNumber) { Packet objectToSendAsPacket = objectToSend as Packet; if (objectToSendAsPacket == null) { using (Packet sendPacket = new Packet(sendingPacketType, objectToSend, options)) SendPacket<sendObjectType>(sendPacket, out packetSequenceNumber); } else { if (objectToSendAsPacket.PacketHeader.PacketType != sendingPacketType) throw new ArgumentException("Unable to send object of type Packet if the PacketHeader.PacketType and sendingPacketType do not match."); SendPacket<sendObjectType>(objectToSendAsPacket, out packetSequenceNumber); } } /// <summary> /// Send an empty packet using the provided packetType. Useful for signalling. /// 根据指定的消息类型 发送一个空数据包 发送信号时有用 /// </summary> /// <param name="sendingPacketType">The sending packet type</param> public void SendObject(string sendingPacketType) { SendObject<object>(sendingPacketType, null); } /// <summary> /// Send an empty packet using the provided packetType. Useful for signalling. /// 根据指定的消息类型 发送一个空数据包 发送信号时有用 /// </summary> /// <param name="sendingPacketType">消息类型 The sending packet type</param> /// <param name="packetSequenceNumber">顺序号 The sequence number of the packet sent</param> public void SendObject(string sendingPacketType, out long packetSequenceNumber) { SendObject<object>(sendingPacketType, null, ConnectionDefaultSendReceiveOptions, out packetSequenceNumber); } /// <summary> /// Send an object using the connection default <see cref="SendReceiveOptions"/> and wait for a returned object /// again using default <see cref="SendReceiveOptions"/>. /// 发送并接收数据 (同步方法) /// </summary> /// <typeparam name="sendObjectType">发送的类的类型 The sending object type, i.e. string, int[], etc</typeparam> /// <typeparam name="returnObjectType">返回的类的类型 The type of return object</typeparam> /// <param name="sendingPacketTypeStr">发送的消息类型The sending packet type</param> /// <param name="expectedReturnPacketTypeStr">预期返回的消息类型The packet type which will be used for the reply</param> /// <param name="returnPacketTimeOutMilliSeconds">消息返回的超时时间 A timeout in milliseconds after which if not reply is received /// will throw an ExpectedReturnTimeoutException.</param> /// <param name="sendObject">发送的对象 The object to send</param> /// <returns>返回的对象 The requested return object</returns> public returnObjectType SendReceiveObject<sendObjectType, returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, sendObjectType sendObject) { return SendReceiveObject<sendObjectType, returnObjectType>(sendingPacketTypeStr, expectedReturnPacketTypeStr, returnPacketTimeOutMilliSeconds, sendObject, null, null); } /// <summary> /// Send an object using the connection default <see cref="SendReceiveOptions"/> and wait for a returned object again /// using default <see cref="SendReceiveOptions"/>. /// 发送并接收数据 (同步方法) /// </summary> /// <typeparam name="sendObjectType">发送的类的类型 The sending object type, i.e. string, int[], etc</typeparam> /// <typeparam name="returnObjectType">返回的类的类型 The type of return object</typeparam> /// <param name="sendingPacketTypeStr">发送的消息类型 The sending packet type</param> /// <param name="expectedReturnPacketTypeStr">预期返回的消息类型 The packet type which will be used for the reply</param> /// <param name="returnPacketTimeOutMilliSeconds">消息返回的超时时间 A timeout in milliseconds after which if not reply is received will /// throw an ExpectedReturnTimeoutException.</param> /// <param name="sendObject">发送的对象 The object to send</param> /// <param name="sentPacketSequenceNumber">包的顺序号 The sequence number of the packet sent</param> /// <returns>返回的对象 The requested return object</returns> public returnObjectType SendReceiveObject<sendObjectType, returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, sendObjectType sendObject, out long sentPacketSequenceNumber) { return SendReceiveObject<sendObjectType, returnObjectType>(sendingPacketTypeStr, expectedReturnPacketTypeStr, returnPacketTimeOutMilliSeconds, sendObject, null, null, out sentPacketSequenceNumber); } /// <summary> /// Send an object using the provided <see cref="SendReceiveOptions"/> and wait for a returned object using provided /// 发送并接收数据 (同步方法) /// </summary> /// <typeparam name="sendObjectType">发送的类的类型 The sending object type, i.e. string, int[], etc</typeparam> /// <typeparam name="returnObjectType">返回的类的类型 The type of return object</typeparam> /// <param name="sendingPacketTypeStr">发送的消息类型 The sending packet type</param> /// <param name="expectedReturnPacketTypeStr">预期返回的消息类型 The packet type which will be used for the reply</param> /// <param name="returnPacketTimeOutMilliSeconds">消息返回的超时时间 A timeout in milliseconds after which if not reply is received will /// throw an ExpectedReturnTimeoutException.</param> /// <param name="sendObject">发送的对象 The object to send</param> /// <param name="sendOptions">发送时的收发参数 SendReceiveOptions to use when sending</param> /// <param name="receiveOptions">接收时的收发参数 SendReceiveOptions used when receiving the return object</param> /// <returns>The requested return object</returns> public returnObjectType SendReceiveObject<sendObjectType, returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, sendObjectType sendObject, SendReceiveOptions sendOptions, SendReceiveOptions receiveOptions) { long sentPacketSequenceNumber; return SendReceiveObject<sendObjectType, returnObjectType>(sendingPacketTypeStr, expectedReturnPacketTypeStr, returnPacketTimeOutMilliSeconds, sendObject, sendOptions, receiveOptions, out sentPacketSequenceNumber); } /// <summary> /// Send an object using the provided <see cref="SendReceiveOptions"/> and wait for a returned object using provided /// 发送并接收数据 (同步方法) /// </summary> /// <typeparam name="sendObjectType">发送的类的类型 The sending object type, i.e. string, int[], etc</typeparam> /// <typeparam name="returnObjectType">返回的类的类型 The type of return object</typeparam> /// <param name="sendingPacketTypeStr">发送的消息类型 The sending packet type</param> /// <param name="expectedReturnPacketTypeStr">预期返回的消息类型 The packet type which will be used for the reply</param> /// <param name="returnPacketTimeOutMilliSeconds">消息返回的超时时间 A timeout in milliseconds after which if not reply is received will /// throw an ExpectedReturnTimeoutException.</param> /// <param name="sendObject">发送的对象 The object to send</param> /// <param name="sendOptions">发送时的收发参数 SendReceiveOptions to use when sending</param> /// <param name="receiveOptions">接收时的收发参数 SendReceiveOptions used when receiving the return object</param> /// <param name="sentPacketSequenceNumber">数据包的顺序号The sequence number of the packet sent</param> /// <returns>The requested return object</returns> public returnObjectType SendReceiveObject<sendObjectType, returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, sendObjectType sendObject, SendReceiveOptions sendOptions, SendReceiveOptions receiveOptions, out long sentPacketSequenceNumber) { if (sendingPacketTypeStr == expectedReturnPacketTypeStr) throw new ArgumentException("The provided sendingPacketTypeStr and expectedReturnPacketTypeStr parameters must be different."); returnObjectType returnObject = default(returnObjectType); bool remotePeerDisconnectedDuringWait = false; AutoResetEvent returnWaitSignal = new AutoResetEvent(false); #region SendReceiveDelegate NetworkComms.PacketHandlerCallBackDelegate<returnObjectType> SendReceiveDelegate = (packetHeader, sourceConnection, incomingObject) => { returnObject = incomingObject; returnWaitSignal.Set(); }; //We use the following delegate to quickly force a response timeout if the remote end disconnects //如果远端掉线 我们使用下面的委托马上触发等待超时 NetworkComms.ConnectionEstablishShutdownDelegate SendReceiveShutDownDelegate = (sourceConnection) => { remotePeerDisconnectedDuringWait = true; returnObject = default(returnObjectType); returnWaitSignal.Set(); }; #endregion if (sendOptions == null) sendOptions = ConnectionDefaultSendReceiveOptions; if (receiveOptions == null) receiveOptions = ConnectionDefaultSendReceiveOptions; AppendShutdownHandler(SendReceiveShutDownDelegate); AppendIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate, receiveOptions); //Check to see if we already have a packet //检测我们是否已经获得数据包 Packet sendObjectAsPacket = sendObject as Packet; if (sendObjectAsPacket == null) { using (Packet sendPacket = new Packet(sendingPacketTypeStr, expectedReturnPacketTypeStr, sendObject, sendOptions)) SendPacket<sendObjectType>(sendPacket, out sentPacketSequenceNumber); } else { if (sendObjectAsPacket.PacketHeader.PacketType != sendingPacketTypeStr) throw new ArgumentException("Unable to send object of type Packet if the PacketHeader.PacketType and sendingPacketType do not match."); SendPacket<sendObjectType>(sendObjectAsPacket, out sentPacketSequenceNumber); } //We wait for the return data here //等待返回的数据 #if NET2 if (!returnWaitSignal.WaitOne(returnPacketTimeOutMilliSeconds, false)) #else if (!returnWaitSignal.WaitOne(returnPacketTimeOutMilliSeconds)) #endif { RemoveIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate); throw new ExpectedReturnTimeoutException("Timeout occurred after " + returnPacketTimeOutMilliSeconds.ToString() + "ms waiting for response packet of type '" + expectedReturnPacketTypeStr + "'."); } RemoveIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate); RemoveShutdownHandler(SendReceiveShutDownDelegate); if (remotePeerDisconnectedDuringWait) throw new ConnectionShutdownException("Remote end closed connection before data was successfully returned."); else return returnObject; } /// <summary> /// Send an empty packet using the connection default <see cref="SendReceiveOptions"/> and wait for a returned object again /// using default <see cref="SendReceiveOptions"/>. Useful to request an object when there is no need to send anything. /// 发送空的数据包给对方 使用默认的收发参数 并等待返回的数据包 /// 当我们从对方获取某个对象,但不需要发送对象给对方时使用 /// </summary> /// <typeparam name="returnObjectType">返回的对象的类型The type of return object</typeparam> /// <param name="sendingPacketTypeStr">消息类型 The sending packet type</param> /// <param name="expectedReturnPacketTypeStr">期待返回的消息类型 The packet type which will be used for the reply</param> /// <param name="returnPacketTimeOutMilliSeconds">超时时间 A timeout in milliseconds after which if not reply is received will throw /// an ExpectedReturnTimeoutException.</param> /// <returns></returns> public returnObjectType SendReceiveObject<returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds) { return SendReceiveObject<object, returnObjectType>(sendingPacketTypeStr, expectedReturnPacketTypeStr, returnPacketTimeOutMilliSeconds, null, null, null); } /// <summary> /// Send an empty packet using the connection default <see cref="SendReceiveOptions"/> and wait for a returned object again using default <see cref="SendReceiveOptions"/>. Usefull to request an object when there is no need to send anything. /// 发送空的数据包给对方 使用默认的收发参数 并等待返回的数据包 /// 当我们从对方获取某个对象,但不需要发送对象给对方时使用 /// </summary> /// <typeparam name="returnObjectType">返回的对象的类型 The type of return object</typeparam> /// <param name="sendingPacketTypeStr">消息类型 The sending packet type</param> /// <param name="expectedReturnPacketTypeStr">期待返回的消息类型 The packet type which will be used for the reply</param> /// <param name="returnPacketTimeOutMilliSeconds">超时时间 A timeout in milliseconds after which if not reply is received will throw an ExpectedReturnTimeoutException.</param> /// <param name="sentPacketSequenceNumber">包的顺序号 The sequence number of the packet sent</param> /// <returns></returns> public returnObjectType SendReceiveObject<returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, out long sentPacketSequenceNumber) { return SendReceiveObject<object, returnObjectType>(sendingPacketTypeStr, expectedReturnPacketTypeStr, returnPacketTimeOutMilliSeconds, null, null, null, out sentPacketSequenceNumber); } /// <summary> /// Closes the connection and trigger any associated shutdown delegates. /// 关闭连接并触发相关的委托 /// </summary> /// <param name="closeDueToError">关闭 是否是由于错误 Closing a connection due an error possibly requires a few extra steps.</param> /// <param name="logLocation">可选的调试参数 Optional debug parameter.</param> ) { try { if (NetworkComms.LoggingEnabled) { if (closeDueToError) NetworkComms.Logger.Debug("Closing connection with " + ConnectionInfo + " due to error from [" + logLocation.ToString() + "]."); else NetworkComms.Logger.Debug("Closing connection with " + ConnectionInfo + " from [" + logLocation.ToString() + "]."); } ConnectionInfo.NoteConnectionShutdown(); //Set possible error cases 设置可能的错误情况 if (closeDueToError) { connectionSetupException = true; connectionSetupExceptionStr = "Connection was closed during setup from [" + logLocation.ToString() + "]."; } //Ensure we are not waiting for a connection to be established if we have died due to error //给connectionSetupWait信号 确保连接关闭后 系统不再等待连接的创建 connectionSetupWait.Set(); //Call any connection specific close requirements //关闭连接的具体方法 CloseConnectionSpecific(closeDueToError, logLocation); #if !NETFX_CORE try { //If we are calling close from the listen thread we are actually in the same thread //We must guarantee the listen thread stops even if that means we need to nuke it //If we did not we may not be able to shutdown properly. //如果我们从监听线程调用Close,我们实际上是在同一个线程 //我们必须保证监听线程停止 //否则我们可能无法正常关闭 if (incomingDataListenThread != null && incomingDataListenThread != Thread.CurrentThread && (incomingDataListenThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin || incomingDataListenThread.ThreadState == System.Threading.ThreadState.Running)) { //If we have made it this far we give the thread a further 50ms to finish before nuking. //如果我们这么做,我们给线程50ms来完成相应的事情 )) { incomingDataListenThread.Abort(); if (NetworkComms.LoggingEnabled && ConnectionInfo != null) NetworkComms.Logger.Warn("Incoming data listen thread with " + ConnectionInfo + " aborted."); } } } catch (Exception) { } #endif //Close connection my get called multiple times for a given connection depending on the reason for being closed //根据关闭的原因 我们可能会多次调用 Close connection bool firstClose = NetworkComms.RemoveConnectionReference(this); try { //Almost there //Last thing is to call any connection specific shutdown delegates //最后来调用连接指定关闭委托 if (firstClose && 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 (firstClose && 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."); } } catch (Exception ex) { #if !NETFX_CORE if (ex is ThreadAbortException) { /*Ignore the threadabort exception if we had to nuke a thread*/ } else #endif LogTools.LogException(ex, "NCError_CloseConnection", "Error closing connection with " + ConnectionInfo + ". Close called from " + logLocation.ToString() + (closeDueToError ? " due to error." : ".")); //We try to rethrow where possible but CloseConnection could very likely be called from within networkComms so we just have to be happy with a log here //记录日志 } } /// <summary> /// Every connection will probably have to perform connection specific shutdown tasks. This is called before the global /// connection close tasks. /// 连接关闭的具体方法 /// </summary> /// <param name="closeDueToError">Closing a connection due an error possibly requires a few extra steps.</param> /// <param name="logLocation">Optional debug parameter for determining the location of the close.</param> ); /// <summary> /// Uses the current connection and returns a bool dependant on the remote end responding to a SendReceiveObject call /// within the default <see cref="NetworkComms.ConnectionAliveTestTimeoutMS"/> /// 连接是否为活动状态 /// </summary> /// <returns>True if the remote end responds within <see cref="NetworkComms.ConnectionAliveTestTimeoutMS"/> otherwise false</returns> public bool ConnectionAlive() { return ConnectionAlive(NetworkComms.ConnectionAliveTestTimeoutMS); } /// <summary> /// Uses the current connection and returns a bool dependant on the remote end responding to a SendReceiveObject call /// within the provided aliveRespondTimeoutMS. /// 连接是否为活动状态 /// </summary> /// <param name="aliveRespondTimeoutMS">超时时间 The time to wait in milliseconds before returning false</param> /// <returns>True if the remote end responds within the provided aliveRespondTimeoutMS</returns> public bool ConnectionAlive(int aliveRespondTimeoutMS) { long responseTime; return ConnectionAlive(aliveRespondTimeoutMS, out responseTime); } /// <summary> /// Uses the current connection and returns a bool dependant on the remote end responding to a SendReceiveObject call /// within the provided aliveRespondTimeoutMS /// 连接是否为活动状态 /// </summary> /// <param name="aliveRespondTimeoutMS">超时时间 The time to wait in milliseconds before returning false</param> /// <param name="responseTimeMS">回复时间 The number of milliseconds taken for a successful response to be received</param> /// <returns></returns> public bool ConnectionAlive(int aliveRespondTimeoutMS, out long responseTimeMS) { System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); responseTimeMS = long.MaxValue; if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled) { //We wait for TCP connections to be established //等待TCP连接创建 if (ConnectionInfo.ConnectionType == ConnectionType.TCP && ConnectionInfo.ConnectionState != ConnectionState.Established) { if ((DateTime.Now - ConnectionInfo.ConnectionCreationTime).Milliseconds > NetworkComms.ConnectionEstablishTimeoutMS) { CloseConnection(); return false; } else return true; } else { try { timer.Start(); SendReceiveObject<], NetworkComms.InternalFixedSendReceiveOptions, NetworkComms.InternalFixedSendReceiveOptions); timer.Stop(); responseTimeMS = timer.ElapsedMilliseconds; if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("ConnectionAliveTest success, response in " + timer.ElapsedMilliseconds.ToString() + "ms."); return true; } catch (Exception) { CloseConnection(); return false; } } } else return false; } /// <summary> /// Send the provided packet to the remoteEndPoint. Waits for receive confirmation if required. /// 发送数据包到远端点上 等待接收确认消息(如果需要) /// </summary> /// <typeparam name="packetPayloadObjectType">The type of object encapsulated by the provided packet</typeparam> /// <param name="packet">The packet to send</param> public void SendPacket<packetPayloadObjectType>(IPacket packet) { long packetSequenceNumber; SendPacket<packetPayloadObjectType>(packet, out packetSequenceNumber); } /// <summary> /// Send the provided packet to the remoteEndPoint. Waits for receive confirmation if required. /// 发送数据包到远端点上 等待接收确认消息(如果需要) /// </summary> /// <typeparam name="packetPayloadObjectType">The type of object encapsulated by the provided packet</typeparam> /// <param name="packet">数据包 The packet to send</param> /// <param name="packetSequenceNumber">顺序号 The sequence number of the packet sent</param> public void SendPacket<packetPayloadObjectType>(IPacket packet, out long packetSequenceNumber) { if (NetworkComms.LoggingEnabled) { string packetDataMD5 = ""; if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.CheckSumHash)) packetDataMD5 = packet.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash); NetworkComms.Logger.Trace("Entering packet send of '" + packet.PacketHeader.PacketType + "' packetType to " + ConnectionInfo + (packetDataMD5 == "" ? "" : ". PacketCheckSum="+packetDataMD5)); } if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired) && ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled) throw new ArgumentException("Provided sendReceiveOptions specified ReceiveConfirmationRequired which is invalid for" + "connections which do not enable the application protocol. Please check provided sendReceiveOptions including global defaults and try again."); //Multiple threads may try to send packets at the same time so wait one at a time here //同步锁 多防止个线程可能同时发送数据包 lock (sendLocker) { //We don't allow sends on a closed connection //不允许在关闭的连接上发送数据 if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown) throw new CommunicationException("Attempting to send packet on connection which has been closed or is currently closing."); //Set packet sequence number inside sendLocker //设置数据包的顺序号 //Increment the global counter as well to ensure future connections with the same host can not create duplicates //顺序号自增长 确保不会重复 Interlocked.Increment(ref NetworkComms.totalPacketSendCount); packetSequenceNumber = packetSequenceCounter++; packet.PacketHeader.SetOption(PacketHeaderLongItems.PacketSequenceNumber, packetSequenceNumber); //string confirmationCheckSum = ""; long expectedPacketSequenceConfirmationNumber = packetSequenceNumber; AutoResetEvent confirmationWaitSignal = new AutoResetEvent(false); bool remotePeerDisconnectedDuringWait = false; #region Delegates //Specify a delegate we may use if we require receive confirmation //指定一个委托,如果需要接收确认消息 NetworkComms.PacketHandlerCallBackDelegate<long> confirmationDelegate = (packetHeader, connectionInfo, incomingSequenceIdentifier) => { //A better method for confirming packets is to use the sending sequence number //确认数据包一个好的方法 即使用发送的顺序号 if (incomingSequenceIdentifier == expectedPacketSequenceConfirmationNumber) confirmationWaitSignal.Set(); }; //We use the following delegate to quickly force a response timeout if the remote end disconnects during a send/wait //如果对方掉线 设置立即超时 NetworkComms.ConnectionEstablishShutdownDelegate ConfirmationShutDownDelegate = (connectionInfo) => { remotePeerDisconnectedDuringWait = true; confirmationWaitSignal.Set(); }; #endregion try { #region Prepare For Confirmation and Possible Validation //Add the confirmation handler if required //如果需要添加确认收到数据包处理器 if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired)) { AppendIncomingPacketHandler(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Confirmation), confirmationDelegate, NetworkComms.InternalFixedSendReceiveOptions); AppendShutdownHandler(ConfirmationShutDownDelegate); } //If this packet is not a checkSumFailResend //如果此数据包的类型不是 checkSumFailResend if (NetworkComms.EnablePacketCheckSumValidation && packet.PacketHeader.PacketType != Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend)) { //We only want to keep packets when they are under some provided threshold //otherwise this becomes a quick 'memory leak' //获取数据包的哈希值 数据包的大小不能大于设定值(NetworkComms.CheckSumMismatchSentPacketCacheMaxByteLimit) if (packet.PacketData.Length < NetworkComms.CheckSumMismatchSentPacketCacheMaxByteLimit) { lock (sentPacketsLocker) { var hash = packet.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash); if (!sentPackets.ContainsKey(hash)) sentPackets.Add(hash, new SentPacket(packet)); } } } #endregion SendPacketSpecific(packet); #region SentPackets Cleanup //If sent packets is greater than 40 we delete anything older than a minute //如果发送的数据包大于40 我们删除一分钟之前发送的数据 lock (sentPacketsLocker) { ) { Dictionary<string, SentPacket> newSentPackets = new Dictionary<string, SentPacket>(); DateTime thresholdTime = DateTime.Now.AddMinutes(-NetworkComms.MinimumSentPacketCacheTimeMinutes); foreach (var storedPacket in sentPackets) { if (storedPacket.Value.SentPacketCreationTime >= thresholdTime) newSentPackets.Add(storedPacket.Key, storedPacket.Value); } sentPackets = newSentPackets; NetworkComms.LastSentPacketCacheCleanup = DateTime.Now; } } #endregion #region Wait For Confirmation If Required //If we required receive confirmation we now wait for that confirmation //如果我们需要对方确认收到消息 在此处等待 if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired)) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... waiting for receive confirmation packet."); #if NET2 if (!(confirmationWaitSignal.WaitOne(NetworkComms.PacketConfirmationTimeoutMS, false))) #else if (!(confirmationWaitSignal.WaitOne(NetworkComms.PacketConfirmationTimeoutMS))) #endif throw new ConfirmationTimeoutException("Confirmation packet timeout."); if (remotePeerDisconnectedDuringWait) throw new ConfirmationTimeoutException("Remote end closed connection before confirmation packet was returned."); else { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... confirmation packet received."); } } #endregion //Update the traffic time as late as possible in case there is a problem //更新ConnectionInfo中的数据包的传输时间 ConnectionInfo.UpdateLastTrafficTime(); } catch (ConfirmationTimeoutException) { //Confirmation timeout there is no need to close the connection as this //does not necessarily mean there is a connection problem //确认消息超时异常 throw; } catch (CommunicationException) { //We close the connection due to communication exceptions //通信异常 CloseConnection(); throw; } catch (TimeoutException ex) { //We close the connection due to communication exceptions //超时异常 if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn("Timeout exception for connection " + this.ConnectionInfo + (ex.Message != null ? ". " +ex.Message : ".")); CloseConnection(); throw new ConnectionSendTimeoutException(ex.ToString()); } catch (Exception ex) { //We close the connection due to communication exceptions //异常 CloseConnection(); throw new CommunicationException(ex.ToString()); } finally { if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired)) { //Clean-up our delegates //清理我们的委托 RemoveIncomingPacketHandler(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Confirmation), confirmationDelegate); RemoveShutdownHandler(ConfirmationShutDownDelegate); } } } if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Completed packet send of '" + packet.PacketHeader.PacketType + "' packetType to " + ConnectionInfo); } /// <summary> /// Implementation for sending a null packets on this connection type. Used for ensuring a connection /// is not terminated by an intermediary switch/router due to inactivity. /// 发送空数据包 /// </summary> private void SendNullPacket() { //We don't send null packets for UDP //在 UDP连接上不发送空数据包 if (ConnectionInfo.ConnectionType == ConnectionType.UDP) return; //We can't send null packets if the application layer is disabled //as we have no way to distinguish them on the receiving side //如果应用层协议禁用 不发送空数据包 因为此时我们没有方法在接收端识别出他们 if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Ignoring null packet send to " + ConnectionInfo + " as the application layer protocol is disabled."); return; } try { //Only once the connection has been established do we send null packets //只有在连接创建完成后,我们发送空数据包 if (ConnectionInfo.ConnectionState == ConnectionState.Established) { //Multiple threads may try to send packets at the same time so we need this lock to prevent a thread cross talk //同步锁 防止多个线程同时发送数据包 lock (sendLocker) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Sending null packet to " + ConnectionInfo); //Send a single 0 byte //发送一个单字节数据 double maxSendTimePerKB = double.MaxValue; if (!NetworkComms.DisableConnectionSendTimeouts) { if (SendTimesMSPerKBCache.Count > MinNumSendsBeforeConnectionSpecificSendTimeout) maxSendTimePerKB = Math.Max(MinimumMSPerKBSendTimeout, SendTimesMSPerKBCache.CalculateMean() + NumberOfStDeviationsForWriteTimeout * SendTimesMSPerKBCache.CalculateStdDeviation()); else maxSendTimePerKB = DefaultMSPerKBSendTimeout; } StreamTools.StreamSendWrapper[] streamsToSend = new StreamTools.StreamSendWrapper[] { }), true)) }; SendStreams(streamsToSend, maxSendTimePerKB, ); //Update the traffic time after we have written to netStream //更新传输时间 ConnectionInfo.UpdateLastTrafficTime(); } } //If the connection is shutdown we should call close //连接关闭 调用CloseConnection方法 ); } catch (Exception) { CloseConnection(); } } /// <summary> /// Send the provided packet /// 发送数据包的具体方法 /// </summary> /// <param name="packet"></param> private void SendPacketSpecific(IPacket packet) { byte[] headerBytes; //Serialise the header //序列化包头 if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled) headerBytes = packet.SerialiseHeader(NetworkComms.InternalFixedSendReceiveOptions); else { //If this connection does not use the application layer protocol we need to check a few things //如果连接没有使用应用层协议 我们需要做一些检测 headerBytes = ]; if (packet.PacketHeader.PacketType != Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged)) throw new UnexpectedPacketTypeException("Only 'Unmanaged' packet types can be used if the NetworkComms.Net application layer protocol is disabled."); ) throw new NotSupportedException("Sending a zero length array if the NetworkComms.Net application layer protocol is disabled is not supported."); } double maxSendTimePerKB = double.MaxValue; if (!NetworkComms.DisableConnectionSendTimeouts) { if (SendTimesMSPerKBCache.Count > MinNumSendsBeforeConnectionSpecificSendTimeout) maxSendTimePerKB = Math.Max(MinimumMSPerKBSendTimeout, SendTimesMSPerKBCache.CalculateMean() + NumberOfStDeviationsForWriteTimeout * SendTimesMSPerKBCache.CalculateStdDeviation()); else maxSendTimePerKB = DefaultMSPerKBSendTimeout; } if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Sending a packet of type '" + packet.PacketHeader.PacketType + "' to " + ConnectionInfo + " containing " + headerBytes.Length.ToString() + " header bytes and " + packet.PacketData.Length.ToString() + " payload bytes. Allowing " + maxSendTimePerKB.ToString("0.0##") + " ms/KB for send."); DateTime startTime = DateTime.Now; StreamTools.StreamSendWrapper[] streamsToSend = new StreamTools.StreamSendWrapper[] { new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(new MemoryStream(headerBytes), true)), packet.PacketData}; ; foreach (StreamTools.StreamSendWrapper stream in streamsToSend) totalBytesToSend += stream.Length; //Send the streams 发送数据流 double[] timings = SendStreams(streamsToSend, maxSendTimePerKB, totalBytesToSend); //Record the timings 记录时间 ; ; i < timings.Length; i++) { timingsSum += timings[i]; SendTimesMSPerKBCache.AddValue(timings[i], streamsToSend[i].Length); } SendTimesMSPerKBCache.TrimList(MaxNumSendTimes); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... " + (totalBytesToSend / 1024.0).ToString("0.000") + "KB sent at average of " + ((totalBytesToSend / 1024.0) / (DateTime.Now - startTime).TotalSeconds).ToString("0.000") + "KB/s. Current:" + (timingsSum / timings.Length).ToString("0.00") + " ms/KB, Connection Avg:" + SendTimesMSPerKBCache.CalculateMean().ToString("0.00") + " ms/KB."); } /// <summary> /// Connection specific implementation for sending data on this connection type. /// Each StreamSendWrapper[] represents a single expected packet. /// 发送数据流 /// </summary> /// <param name="streamsToSend">数据流 The streams which need to be sent</param> /// <param name="maxSendTimePerKB">每KB数据最长的发送时间 The maximum time to allow per KB before a write timeout exception.</param> /// <param name="totalBytesToSend">总共需要发送的字节数量 A precalculated sum of streams.Length</param> /// <returns>Should return double[] which represents the milliseconds per byte written for each StreamSendWrapper</returns> protected abstract double[] SendStreams(StreamTools.StreamSendWrapper[] streamsToSend, double maxSendTimePerKB, long totalBytesToSend); /// <summary> /// Dispose of the connection. Recommended usage is to call CloseConnection instead. /// 释放连接 推荐使用CloseConnection替代 /// </summary> public void Dispose() { CloseConnection(); try { ((IDisposable)connectionSetupWait).Dispose(); ((IDisposable)connectionEstablishWait).Dispose(); } catch (Exception) { } } }
介绍开源的.net通信框架NetworkComms框架 源码分析(十七 ) ConnectionSendClose的更多相关文章
- DotNetty网络通信框架学习之源码分析
DotNetty网络通信框架学习之源码分析 有关DotNetty框架,网上的详细资料不是很多,有不多的几个博友做了简单的介绍,也没有做深入的探究,我也根据源码中提供的demo做一下记录,方便后期查阅. ...
- 深入理解分布式调度框架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 ...
- Laravel开发:Laravel框架门面Facade源码分析
前言 这篇文章我们开始讲 laravel 框架中的门面 Facade,什么是门面呢?官方文档: Facades(读音:/fəˈsäd/ )为应用程序的服务容器中可用的类提供了一个「静态」接口.Lara ...
- Android 应用框架层 SQLite 源码分析
概述 Android 在应用框架层为开发者提供了 SQLite 相关操作接口,其归属于android.database.sqlite包底下,主要包含SQLiteProgram, SQLiteDat ...
随机推荐
- java.sql.SQLException: No suitable driver 问题解决
最近在学习java,用到c3p0数据库连接池,遇到一个很奇怪的现象,用main方法测试是可以正常连接数据库的,但是使用jsp调用代码,就会报如下图的错误! 最下面的java.sql.SQLExcept ...
- 推荐10个很棒的AngularJS学习指南
AngularJS 是非常棒的JS框架,能够创建功能强大,动态功能的Web app.AngularJS自2009发布以来,已经广泛应用于Web 开发中.但是对想要学习Angular JS 的人而言,只 ...
- linux网络编程系列-网络连接的建立
一个比较实用的连接函数,支持host为域名. #include <netdb.h> #include <sys/socket.h> #include <sys/types ...
- 关于redis启动流程介绍
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/94.html?1455870894 1. 准备运行环境 * 设置oom h ...
- GridView和DATAGRID前后台查询用法的比较
Grideview前台: <DIV class="mainDiv" id="GridWidth"> <ASP:GridView id=&quo ...
- 将图片的二进制字节字符串在HTML页面以图片形式输出
具体实现代码如下: 1.新建一个一般处理程序: Image.ashx using System; using System.Collections.Generic; using System.Linq ...
- AngularJS $http配置为form data 提交
AngularJS $http配置为form data 提交 $scope.formData = {}; $http({ method: 'POST', url: '/user/', // pass ...
- PHP两种redirect
PHP两种redirect redirect header('Location: /admin_data.php'); exit(); redirect `echo "<script& ...
- Java 集合 — HashMap
HashMap 无序(每次resize的时候都会变) 非线程安全 key和value都看可以为null 使用数组和链表实现 查找元素的时候速度快 几个重要属性: loadFactor:用来计算thre ...
- MySQL(四) 数据表的插入、更新、删除数据
序言 数据表的插入.更新.删除非常简单,但是简单的也要学习,细节决定成败. ---WH 一.插入数据 格式:INSERT INTO 表名(字段名...)VALUES(值...); 创建环境 使用per ...