介绍开源的.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 ...
随机推荐
- 使用mybatis访问sql server
原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com mybatis作为一种半自动化的ORM工具,可以提供更大的灵活性,逐渐受到社区的欢迎. 官方下载地址是:https ...
- fir.im Weekly - 当技术成为一种 “武器”
最近纷纷扰扰,快播公开庭审,携程事件仍在升级,百度还在继续无底线.我们相信技术本身并不可耻,但是用技术作恶就是可耻.当技术成为一种武器,Do not be evil. 好了,继续本期的 fir.im ...
- salesforce 零基础学习(四十二)简单文件上传下载
项目中,常常需要用到文件的上传和下载,上传和下载功能实际上是对Document对象进行insert和查询操作.本篇演示简单的文件上传和下载,理论上文件上传后应该将ID作为操作表的字段存储,这里只演示文 ...
- Android TextView 常用技巧
Android ListView 常用技巧 Android TextView 常用技巧 TextView在Android中实现文字说明等功能,基本的使用都很简单,那么除了基本展示文字的使用,我们还能够 ...
- KnockoutJS 3.X API 第四章 表单绑定(10) textInput、hasFocus、checked绑定
textInput绑定目的 textInput绑定主要用于<input>或者<textarea>元素.他提供了DOM和viewmodel的双向更新.不同于value绑定,tex ...
- XML学习笔记7——XSD实例
在前面的XSD笔记中,基本上是以数据类型为主线来写的,而在我的实际开发过程中,是先设计好了XML的结构(元素.属性),并写好了一份示例,然后再反过来写XSD文件(在工具生成的基础上修改),也就是说,是 ...
- Ubuntu16配置静态IP
一.静态IP地址配置 sudo vi /etc/network/interfaces 然后按照如下格式修改: 注意这里的网卡名字是ens33 auto lo iface lo inet loopbac ...
- Jetty 服务器的知识
Jetty 服务器的知识 也许你还没有听说过这个Jetty服务器,不过它确实是一种比较轻量级的Java服务器,和Tomcat一样,专门解释JavaWeb程序的服务器.因为在之前学习Java Web 的 ...
- 简明 Git 命令速查表
本文总结了git常用的命令,以便学习者使用时查阅~ 几个专用名词的译名如下 Workspace:工作区 Index / Stage:暂存区 Repository:仓库区(或本地仓库) Remote ...
- Unity3D重要知识点
数据结构和算法很重要!图形学也很重要!大的游戏公司很看重个人基础,综合能力小公司看你实际工作能力,看你的Demo. 1.什么是渲染管道? 是指在显示器上为了显示出图像而经过的一系列必要操作. 渲染管道 ...