介绍开源的.net通信框架NetworkComms框架 源码分析(二十三 )TCPConnection
原文网址: http://www.cnblogs.com/csdev
Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是:Apache License v2
开源地址是:https://github.com/MarcFletcher/NetworkComms.Net
/// <summary>
/// A connection object which utilises <see href="http://en.wikipedia.org/wiki/Transmission_Control_Protocol">TCP</see> to communicate between peers.
/// Tcp连接对象 通过TCP协议在端点之间通信
/// </summary>
public sealed partial class TCPConnection : IPConnection
{
#if WINDOWS_PHONE || NETFX_CORE
/// <summary>
/// The windows phone socket corresponding to this connection.
/// </summary>
StreamSocket socket;
#else
/// <summary>
/// The TcpClient corresponding to this connection.
/// 与此连接相关的TcpClient对象
/// </summary>
TcpClient tcpClient;
/// <summary>
/// The networkstream associated with the tcpClient.
/// 与tcpClient相对应额networkstream对象
/// </summary>
Stream connectionStream;
/// <summary>
/// The SSL options associated with this connection.
/// SSL(安全套接层) 相关的参数设置
/// </summary>
public SSLOptions SSLOptions { get; private set; }
#endif
/// <summary>
/// The current incoming data buffer
/// 进入的数据的缓冲区
/// </summary>
byte[] dataBuffer;
/// <summary>
/// TCP connection constructor
/// TCP连接构造器
/// </summary>
#if WINDOWS_PHONE || NETFX_CORE
private TCPConnection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions, StreamSocket socket)
#else
private TCPConnection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions, TcpClient tcpClient, SSLOptions sslOptions)
#endif
: base(connectionInfo, defaultSendReceiveOptions)
{
if (connectionInfo.ConnectionType != ConnectionType.TCP)
throw new ArgumentException("Provided connectionType must be TCP.", "connectionInfo");
dataBuffer = new byte[NetworkComms.InitialReceiveBufferSizeBytes];
//We don't guarantee that the tcpClient has been created yet
//在构造器中 tcpClient对象可能还没有被创建
#if WINDOWS_PHONE || NETFX_CORE
if (socket != null) this.socket = socket;
#else
if (tcpClient != null) this.tcpClient = tcpClient;
this.SSLOptions = sslOptions;
#endif
}
/// <inheritdoc />
/// 创建Tcp连接
protected override void EstablishConnectionSpecific()
{
#if WINDOWS_PHONE || NETFX_CORE
if (socket == null) ConnectSocket();
//For the local endpoint
//本地端点
var localEndPoint = new IPEndPoint(IPAddress.Parse(socket.Information.LocalAddress.CanonicalName.ToString()), int.Parse(socket.Information.LocalPort));
//We should now be able to set the connectionInfo localEndPoint
//此处 可以设置connectionInfo对象的本地端点
NetworkComms.UpdateConnectionReferenceByEndPoint(this, ConnectionInfo.RemoteIPEndPoint, localEndPoint);
ConnectionInfo.UpdateLocalEndPointInfo(localEndPoint);
//Set the outgoing buffer size
//设置发送缓冲区大小
socket.Control.OutboundBufferSizeInBytes = (uint)NetworkComms.SendBufferSizeBytes;
#else
if (tcpClient == null) ConnectSocket();
//We should now be able to set the connectionInfo localEndPoint
//我们现在应该能够设置 connectionInfo对象的本地端点
NetworkComms.UpdateConnectionReferenceByEndPoint(this, ConnectionInfo.RemoteIPEndPoint, (IPEndPoint)tcpClient.Client.LocalEndPoint);
ConnectionInfo.UpdateLocalEndPointInfo((IPEndPoint)tcpClient.Client.LocalEndPoint);
if (SSLOptions.SSLEnabled)
ConfigureSSLStream();
else
//We are going to be using the networkStream quite a bit so we pull out a reference once here
//networkStream类型的网络流 后面会有不少地方需要使用 此处做一个引用
connectionStream = tcpClient.GetStream();
//When we tell the socket/client to close we want it to do so immediately 当我们告诉socket/client关闭时 我们希望其立即关闭
//this.tcpClient.LingerState = new LingerOption(false, 0);
//We need to set the keep alive option otherwise the connection will just die at some random time should we not be using it
//我们需要设定Keep alive选项 以防止连接断开 (后来没有使用)
//NOTE: This did not seem to work reliably so was replaced with the keepAlive packet feature
//注意:设定KeepAlive选项来维护Tcp连接并不可靠 所以后来我们用发送心跳包进行代替
//this.tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
tcpClient.ReceiveBufferSize = NetworkComms.MaxReceiveBufferSizeBytes;
tcpClient.SendBufferSize = NetworkComms.SendBufferSizeBytes;
//This disables the 'nagle algorithm' 禁用 nagle算法
//http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.nodelay.aspx
//Basically we may want to send lots of small packets (<200 bytes) and sometimes those are time critical (e.g. when establishing a connection)
//If we leave this enabled small packets may never be sent until a suitable send buffer length threshold is passed. i.e. BAD
//下面的2个选项设定为true,当Tcp连接上有数据时,立即发送,我符合我们的预期。而不是等待Tcp缓冲池中有足够多的数据才一起发送
tcpClient.NoDelay = true;
tcpClient.Client.NoDelay = true;
#endif
//Start listening for incoming data
//开始监听进来的数据
StartIncomingDataListen();
//If the application layer protocol is enabled we handshake the connection
//如果应用层协议启用 连接进行握手操作
//应用层协议,系统默认为启用
if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled)
ConnectionHandshake();
else
{
//If there is no handshake we can now consider the connection established
//如果此处无握手操作 我们可以考虑创建连接
TriggerConnectionEstablishDelegates();
//Trigger any connection setup waits
//触发任何连接建设等待
connectionSetupWait.Set();
}
#if !WINDOWS_PHONE && !NETFX_CORE
//Once the connection has been established we may want to re-enable the 'nagle algorithm' used for reducing network congestion (apparently).
//By default we leave the nagle algorithm disabled because we want the quick through put when sending small packets
//一旦连接建立了,某些情况下我们可能要重新启用“Nagle算法”用于减少网络拥塞。
//默认情况下我们不启用 nagle 算法,启用nagle算法将导致小的数据不能被及时的发送
if (EnableNagleAlgorithmForNewConnections)
{
tcpClient.NoDelay = false;
tcpClient.Client.NoDelay = false;
}
#endif
}
/// <summary>
/// If we were not provided with a tcpClient on creation we need to create one
/// 如果没有tcpClient对象则创建一个
/// </summary>
private void ConnectSocket()
{
try
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Connecting TCP client with " + ConnectionInfo);
bool connectSuccess = true;
#if WINDOWS_PHONE || NETFX_CORE
//We now connect to our target
//我们现在开始连接到我们的目标
socket = new StreamSocket();
socket.Control.NoDelay = !EnableNagleAlgorithmForNewConnections;
CancellationTokenSource cancelAfterTimeoutToken = new CancellationTokenSource(NetworkComms.ConnectionEstablishTimeoutMS);
try
{
if (ConnectionInfo.LocalEndPoint != null && ConnectionInfo.LocalIPEndPoint.Address != IPAddress.IPv6Any && ConnectionInfo.LocalIPEndPoint.Address != IPAddress.Any)
{
var endpointPairForConnection = new Windows.Networking.EndpointPair(new Windows.Networking.HostName(ConnectionInfo.LocalIPEndPoint.Address.ToString()), ConnectionInfo.LocalIPEndPoint.Port.ToString(),
new Windows.Networking.HostName(ConnectionInfo.RemoteIPEndPoint.Address.ToString()), ConnectionInfo.RemoteIPEndPoint.Port.ToString());
var task = socket.ConnectAsync(endpointPairForConnection).AsTask(cancelAfterTimeoutToken.Token);
task.Wait();
}
else
{
var task = socket.ConnectAsync(new Windows.Networking.HostName(ConnectionInfo.RemoteIPEndPoint.Address.ToString()), ConnectionInfo.RemoteIPEndPoint.Port.ToString()).AsTask(cancelAfterTimeoutToken.Token);
task.Wait();
}
}
catch (Exception)
{
socket.Dispose();
connectSuccess = false;
}
#else
//We now connect to our target
//我们现在开始连接到我们的目标
tcpClient = new TcpClient(ConnectionInfo.RemoteEndPoint.AddressFamily);
//Start the connection using the async version
//以异步方式开始连接
//This allows us to choose our own connection establish timeout
//允许设定连接创建超时时间
IAsyncResult ar = tcpClient.BeginConnect(ConnectionInfo.RemoteIPEndPoint.Address, ConnectionInfo.RemoteIPEndPoint.Port, null, null);
WaitHandle connectionWait = ar.AsyncWaitHandle;
try
{
if (!connectionWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS, false))
connectSuccess = false;
else
tcpClient.EndConnect(ar);
}
finally
{
connectionWait.Close();
}
#endif
if (!connectSuccess) throw new ConnectionSetupException("Timeout waiting for remoteEndPoint to accept TCP connection.");
}
catch (Exception ex)
{
CloseConnection();
throw new ConnectionSetupException("Error during TCP connection establish with destination (" + ConnectionInfo + "). Destination may not be listening or connect timed out. " + ex.ToString());
}
}
/// <inheritdoc />
/// 开始监听进入的数据局
protected override void StartIncomingDataListen()
{
if (!NetworkComms.ConnectionExists(ConnectionInfo.RemoteIPEndPoint, ConnectionInfo.LocalIPEndPoint, ConnectionType.TCP, ConnectionInfo.ApplicationLayerProtocol))
{
CloseConnection();
throw new ConnectionSetupException("A connection reference by endPoint should exist before starting an incoming data listener.");
}
#if WINDOWS_PHONE
var stream = socket.InputStream.AsStreamForRead();
stream.BeginRead(dataBuffer, , dataBuffer.Length, new AsyncCallback(IncomingTCPPacketHandler), stream);
#elif NETFX_CORE
Task readTask = new Task(async () =>
{
var buffer = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(dataBuffer);
var readBuffer = await socket.InputStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.Partial);
await IncomingTCPPacketHandler(readBuffer);
});
readTask.Start();
#else
lock (SyncRoot)
{
//以同步方式
if (NetworkComms.ConnectionListenModeUseSync)
{
if (incomingDataListenThread == null)
{
//创建一个线程
incomingDataListenThread = new Thread(IncomingTCPDataSyncWorker);
//Incoming data always gets handled in a time critical fashion
//设定数据处理的优先级
incomingDataListenThread.Priority = NetworkComms.timeCriticalThreadPriority;
incomingDataListenThread.Name = "UDP_IncomingDataListener";
incomingDataListenThread.IsBackground = true;
incomingDataListenThread.Start();
}
}
//以异步方式处理
else
{
if (asyncListenStarted) throw new ConnectionSetupException("Async listen already started. Why has this been called twice?.");
asyncListenerInRead = true;
//开始读取连接上的数据
connectionStream.BeginRead(dataBuffer, , dataBuffer.Length, new AsyncCallback(IncomingTCPPacketHandler), connectionStream);
asyncListenStarted = true;
}
}
#endif
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Listening for incoming data from " + ConnectionInfo);
}
/// <summary>
/// Asynchronous incoming connection data delegate
/// 以异步的方式处理连接上的数据
/// </summary>
/// <param name="ar">The call back state object</param> 回调状态对象
#if NETFX_CORE
private async Task IncomingTCPPacketHandler(IBuffer buffer)
#else
private void IncomingTCPPacketHandler(IAsyncResult ar)
#endif
{
//Initialised with false so that logic still works in WP8
//初始化状态为false 所以可以在wp8系统下运行
bool dataAvailable = false;
#if !WINDOWS_PHONE && !NETFX_CORE
//Incoming data always gets handled in a timeCritical fashion at this point
//Windows phone and RT platforms do not support thread priorities
//设定数据处理的优先级
//wp 和rt平台不支持线程优先级的设定
Thread.CurrentThread.Priority = NetworkComms.timeCriticalThreadPriority;
#endif
try
{
#if WINDOWS_PHONE
Stream stream = ar.AsyncState as Stream;
totalBytesRead = stream.EndRead(ar) + totalBytesRead;
#elif NETFX_CORE
buffer.CopyTo(, dataBuffer, totalBytesRead, (int)buffer.Length);
totalBytesRead = (int)buffer.Length + totalBytesRead;
#else
Stream stream;
if (SSLOptions.SSLEnabled)
stream = (SslStream)ar.AsyncState;
else
stream = (NetworkStream)ar.AsyncState;
if (!stream.CanRead)
throw new ObjectDisposedException("Unable to read from stream.");
if (!asyncListenerInRead) throw new InvalidDataException("The asyncListenerInRead flag should be true. 1");
totalBytesRead = stream.EndRead(ar) + totalBytesRead;
asyncListenerInRead = false;
if (SSLOptions.SSLEnabled)
//SSLstream does not have a DataAvailable property. We will just assume false.
//SSLstream (安全套接层)数据流 不支持DataAvailable属性
dataAvailable = false;
else
dataAvailable = ((NetworkStream)stream).DataAvailable;
#endif
)
{
ConnectionInfo.UpdateLastTrafficTime();
//If we have read a single byte which is 0 and we are not expecting other data
//如果只读取到一个字节,内容为0 (猜测这样的单字节一般用于心跳检测)
if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled &&
totalBytesRead == && dataBuffer[] == && packetBuilder.TotalBytesExpected - packetBuilder.TotalBytesCached == )
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... null packet removed in IncomingPacketHandler() from " + ConnectionInfo + ". 1");
}
else
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... " + totalBytesRead.ToString() + " bytes added to packetBuilder for " + ConnectionInfo + ". Cached " + packetBuilder.TotalBytesCached.ToString() + " bytes, expecting " + packetBuilder.TotalBytesExpected.ToString() + " bytes.");
//If there is more data to get then add it to the packets lists;
//packetBuilder 数据包创建器
//把读取到的字节添加到"数据包创建器"中
//totalBytesRead 此次读取到的字节数量(int类型) dataBuffer (byte[] 字节数组)
packetBuilder.AddPartialPacket(totalBytesRead, dataBuffer);
#if !WINDOWS_PHONE && !NETFX_CORE
//If we have more data we might as well continue reading synchronously
//In order to deal with data as soon as we think we have sufficient we will leave this loop
//如果我们有更多的数据 继续同步读取
//如果接收到的数据足够多,将离开此循环
//根据networkcomms的机制,第一个字节为数据包包头大小,系统会根据数据包包头大小解析出数据包包头,从数据包包头中读取数据包大小
//这个数据包大小就是packetBuilder中totalBytesExpected,即期望的大小
while (dataAvailable && packetBuilder.TotalBytesCached < packetBuilder.TotalBytesExpected)
{
;
//We need a buffer for our incoming data
//First we try to reuse a previous buffer
//我们需要为进入的数据准备缓冲区
//首先我们尝试使用以前的缓冲区
&& packetBuilder.NumUnusedBytesMostRecentPartialPacket() > )
dataBuffer = packetBuilder.RemoveMostRecentPartialPacket(ref bufferOffset);
else
//If we have nothing to reuse we allocate a new buffer. As we are in this loop this can only be a suplementary buffer for THIS packet.
//Therefore we choose a buffer size between the initial amount and the maximum amount based on the expected size
//如果没有可重复使用的缓冲区,我们将新分配一个新的缓冲区
//缓冲区的大小为 数据包创建器.期待的大小 减去 数据包创建器.已收到的大小 (即 数据包长度 减去 已经接收的长度)
{
long additionalBytesNeeded = packetBuilder.TotalBytesExpected - packetBuilder.TotalBytesCached;
dataBuffer = new byte[Math.Max(Math.Min(additionalBytesNeeded, NetworkComms.MaxReceiveBufferSizeBytes), NetworkComms.InitialReceiveBufferSizeBytes)];
}
totalBytesRead = stream.Read(dataBuffer, bufferOffset, dataBuffer.Length - bufferOffset) + bufferOffset;
)
{
ConnectionInfo.UpdateLastTrafficTime();
//If we have read a single byte which is 0 and we are not expecting other data
//处理单字节数据 一般为心跳消息
&& dataBuffer[] == && packetBuilder.TotalBytesExpected - packetBuilder.TotalBytesCached == )
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... null packet ignored in IncomingPacketHandler() from " + ConnectionInfo + ". 2");
//LastTrafficTime = DateTime.Now;
}
else
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... " + totalBytesRead.ToString() + " bytes added to packetBuilder for " + ConnectionInfo + ". Cached " + packetBuilder.TotalBytesCached.ToString() + " bytes, expecting " + packetBuilder.TotalBytesExpected.ToString() + " bytes.");
packetBuilder.AddPartialPacket(totalBytesRead, dataBuffer);
if (SSLOptions.SSLEnabled)
//SSLstream does not have a DataAvailable property. We will just assume false.
//SSLstream 没有DataAvailable属性
dataAvailable = false;
else
dataAvailable = ((NetworkStream)stream).DataAvailable;
}
}
else
break;
}
#endif
}
}
&& packetBuilder.TotalBytesCached >= packetBuilder.TotalBytesExpected)
{
//Once we think we might have enough data we call the incoming packet handle hand off
//Should we have a complete packet this method will start the appropriate task
//This method will now clear byes from the incoming packets if we have received something complete.
//一旦我们的数据包创建器(packetBuilder)中接收到足够一个数据包的数据,我们将会对数据进行处理
//IncomingPacketHandleHandOff方法处理完packetBuilder中相应的数据后,会把已经处理完的数据删除掉
IncomingPacketHandleHandOff(packetBuilder);
}
&& (!dataAvailable || ConnectionInfo.ConnectionState == ConnectionState.Shutdown))
CloseConnection();
else
{
//We need a buffer for our incoming data
//First we try to reuse a previous buffer
//我们需要一个缓冲区来接收数据
//我们先尝试使用以前的缓冲区
&& packetBuilder.NumUnusedBytesMostRecentPartialPacket() > )
dataBuffer = packetBuilder.RemoveMostRecentPartialPacket(ref totalBytesRead);
else
{
//If we have nothing to reuse we allocate a new buffer
//If packetBuilder.TotalBytesExpected is 0 we know we're going to start waiting for a fresh packet. Therefore use the initial buffer size
//如果没有可重复使用的缓冲区,我们将新分配一个新的缓冲区
//如果 数据包创建器 期待的数据大小为0,我们将开始接收一个新的数据包的数据 此时 把缓冲区设置为初始大小
)
dataBuffer = new byte[NetworkComms.InitialReceiveBufferSizeBytes];
else
//Otherwise this can only be a supplementary buffer for THIS packet. Therefore we choose a buffer size between the initial amount and the maximum amount based on the expected size
//如果没有可重复使用的缓冲区,我们将新分配一个新的缓冲区
//缓冲区的大小为 数据包创建器.期待的大小 减去 数据包创建器.已收到的大小 (即 数据包长度 减去 已经接收的长度)
{
long additionalBytesNeeded = packetBuilder.TotalBytesExpected - packetBuilder.TotalBytesCached;
dataBuffer = new byte[Math.Max(Math.Min(additionalBytesNeeded, NetworkComms.MaxReceiveBufferSizeBytes), NetworkComms.InitialReceiveBufferSizeBytes)];
}
totalBytesRead = ;
}
#if NETFX_CORE
IBuffer newBuffer = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(dataBuffer);
var task = IncomingTCPPacketHandler(await socket.InputStream.ReadAsync(newBuffer, newBuffer.Capacity - (uint)totalBytesRead, InputStreamOptions.Partial));
#elif WINDOWS_PHONE
stream.BeginRead(dataBuffer, totalBytesRead, dataBuffer.Length - totalBytesRead, IncomingTCPPacketHandler, stream);
#else
if (asyncListenerInRead) throw new InvalidDataException("The asyncListenerInRead flag should be false. 2");
asyncListenerInRead = true;
stream.BeginRead(dataBuffer, totalBytesRead, dataBuffer.Length - totalBytesRead, IncomingTCPPacketHandler, stream);
#endif
}
}
catch (IOException)
{
CloseConnection();
}
catch (ObjectDisposedException)
{
CloseConnection();
}
catch (SocketException)
{
CloseConnection();
}
catch (InvalidOperationException)
{
CloseConnection();
}
catch (Exception ex)
{
LogTools.LogException(ex, "Error_TCPConnectionIncomingPacketHandler");
CloseConnection();
}
#if !WINDOWS_PHONE && !NETFX_CORE
Thread.CurrentThread.Priority = ThreadPriority.Normal;
#endif
}
#if !WINDOWS_PHONE && !NETFX_CORE
/// <summary>
/// Synchronous incoming connection data worker
/// 同步处理连接上进入的数据的处理器
/// </summary>
private void IncomingTCPDataSyncWorker()
{
bool dataAvailable = false;
try
{
while (true)
{
if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown)
break;
;
//We need a buffer for our incoming data
//First we try to reuse a previous buffer
//我们需要为进入的数据准备缓冲区
//首先我们尝试使用以前的缓冲区
&& packetBuilder.NumUnusedBytesMostRecentPartialPacket() > )
dataBuffer = packetBuilder.RemoveMostRecentPartialPacket(ref bufferOffset);
else
{
//If we have nothing to reuse we allocate a new buffer
//If packetBuilder.TotalBytesExpected is 0 we know we're going to start waiting for a fresh packet. Therefore use the initial buffer size
//如果没有可重复使用的缓冲区,我们将新分配一个新的缓冲区
//如果 数据包创建器 期待的数据大小为0,我们将开始接收一个新的数据包的数据 此时 把缓冲区设置为初始大小
)
dataBuffer = new byte[NetworkComms.InitialReceiveBufferSizeBytes];
else
//Otherwise this can only be a supplementary buffer for THIS packet. Therefore we choose a buffer size between the initial amount and the maximum amount based on the expected size
//如果没有可重复使用的缓冲区,我们将新分配一个新的缓冲区
//缓冲区的大小为 数据包创建器.期待的大小 减去 数据包创建器.已收到的大小 (即 数据包长度 减去 已经接收的长度)
{
long additionalBytesNeeded = packetBuilder.TotalBytesExpected - packetBuilder.TotalBytesCached;
dataBuffer = new byte[Math.Max(Math.Min(additionalBytesNeeded, NetworkComms.MaxReceiveBufferSizeBytes), NetworkComms.InitialReceiveBufferSizeBytes)];
}
}
//We block here until there is data to read
//When we read data we read until method returns or we fill the buffer length
//程序将会阻塞 直到读取到数据
//我们读取数据 直到方法返回或者读取到缓冲区大小的数据
totalBytesRead = connectionStream.Read(dataBuffer, bufferOffset, dataBuffer.Length - bufferOffset) + bufferOffset;
//Check to see if there is more data ready to be read
//检查是否还有数据需要读取
if (SSLOptions.SSLEnabled)
//SSLstream does not have a DataAvailable property. We will just assume false.
//SSLStream没有DataAvailable属性
dataAvailable = false;
else
dataAvailable = ((NetworkStream)connectionStream).DataAvailable;
//If we read any data it gets handed off to the packetBuilder
//如果我们读取到数据,交给packetBuilder处理
)
{
ConnectionInfo.UpdateLastTrafficTime();
//If we have read a single byte which is 0 and we are not expecting other data
//处理单字节 内容为0 的数据 (一般为心跳检测)
&& dataBuffer[] == && packetBuilder.TotalBytesExpected - packetBuilder.TotalBytesCached == )
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... null packet removed in IncomingDataSyncWorker() from "+ConnectionInfo+".");
}
else
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... " + totalBytesRead.ToString() + " bytes added to packetBuilder for " + ConnectionInfo + ". Cached " + packetBuilder.TotalBytesCached.ToString() + " bytes, expecting " + packetBuilder.TotalBytesExpected.ToString() + " bytes.");
//添加数据到 数据包创建器中
packetBuilder.AddPartialPacket(totalBytesRead, dataBuffer);
}
}
&& (!dataAvailable || ConnectionInfo.ConnectionState == ConnectionState.Shutdown))
{
//If we read 0 bytes and there is no data available we should be shutting down
//如果我们没有读取到数据 应该关闭连接
CloseConnection();
break;
}
//If we have read some data and we have more or equal what was expected we attempt a data hand off
//如果我们读取到足够的数据(可以解析出一个数据包),对数据进行处理
&& packetBuilder.TotalBytesCached >= packetBuilder.TotalBytesExpected)
IncomingPacketHandleHandOff(packetBuilder);
}
}
//On any error here we close the connection
//捕捉异常 关闭连接
catch (NullReferenceException)
{
CloseConnection();
}
catch (IOException)
{
CloseConnection();
}
catch (ObjectDisposedException)
{
CloseConnection();
}
catch (SocketException)
{
CloseConnection();
}
catch (InvalidOperationException)
{
CloseConnection();
}
catch (Exception ex)
{
LogTools.LogException(ex, "Error_TCPConnectionIncomingPacketHandler");
CloseConnection();
}
//Clear the listen thread object because the thread is about to end
//清除监听线程 因为线程将关闭
incomingDataListenThread = null;
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Incoming data listen thread ending for " + ConnectionInfo);
}
/// <summary>
/// Configure the SSL stream from this connection
/// 配置当期连接上 SSL 数据流
/// </summary>
private void ConfigureSSLStream()
{
try
{
if (ConnectionInfo.ServerSide)
{
connectionStream = new SslStream(tcpClient.GetStream(), false,
new RemoteCertificateValidationCallback(CertificateValidationCallback),
new LocalCertificateSelectionCallback(CertificateSelectionCallback));
((SslStream)connectionStream).AuthenticateAsServer(SSLOptions.Certificate, SSLOptions.RequireMutualAuthentication, SslProtocols.Default, false);
}
else
{
X509CertificateCollection certs = new X509CertificateCollection();
if (SSLOptions.Certificate != null) certs.Add(SSLOptions.Certificate);
//If we have a certificate set we use that to authenticate
connectionStream = new SslStream(tcpClient.GetStream(), false,
new RemoteCertificateValidationCallback(CertificateValidationCallback),
new LocalCertificateSelectionCallback(CertificateSelectionCallback));
((SslStream)connectionStream).AuthenticateAsClient(SSLOptions.CertificateName, certs, SslProtocols.Default, false);
}
}
catch (AuthenticationException ex)
{
throw new ConnectionSetupException("SSL authentication failed. Please check configuration and try again.", ex);
}
SSLOptions.Authenticated = true;
}
/// <summary>
/// Callback used to determine if the provided certificate should be accepted
/// 回调方法 用于确定是否应该接受所提供的证书
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
private bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNotAvailable && ConnectionInfo.ServerSide)
//If the client did not provide a remote certificate it may well be because
//we were not requesting one
//如果客户没有提供远程认证它很可能是因为我们没有要求
return !SSLOptions.RequireMutualAuthentication;
else if (SSLOptions.AllowSelfSignedCertificate && //If we allows self signed certificates we make sure the errors are correct 如果我们允许自签名认证 我们确定这些错误是正确的
chain.ChainStatus.Length == && //Only a single chain error 只是一个单一的链错误
sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors &&
chain.ChainStatus[].Status == X509ChainStatusFlags.UntrustedRoot)
{
//If we have a local certificate we compare them 如果我们有一个本地的证书 我们比较他们
if (SSLOptions.Certificate != null)
return certificate.Equals(SSLOptions.Certificate);
else
return true;
}
else
return false;
}
/// <summary>
/// Certificate selection callback
/// 证书选择 回调方法
/// </summary>
/// <param name="sender"></param>
/// <param name="targetHost"></param>
/// <param name="localCertificates"></param>
/// <param name="remoteCertificate"></param>
/// <param name="acceptableIssuers"></param>
/// <returns></returns>
private X509Certificate CertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers)
{
return SSLOptions.Certificate;
}
#endif
/// <inheritdoc />
)
{
#if WINDOWS_PHONE || NETFX_CORE
//Try to close the socket
try
{
socket.Dispose();
}
catch (Exception)
{
}
#else
//The following attempts to correctly close the connection
//Try to close the networkStream first
//以下试图正确的关闭连接
//首先尝试关闭网络流
try
{
if (connectionStream != null) connectionStream.Close();
}
catch (Exception)
{
}
finally
{
connectionStream = null;
}
//Try to close the tcpClient
//尝试关闭tcpClient对象
try
{
if (tcpClient.Client!=null)
{
tcpClient.Client.Disconnect(false);
#if !ANDROID
//Throws uncatchable exception in android
//在安卓系统中抛出不可捕捉的异常
tcpClient.Client.Close();
#endif
}
}
catch (Exception)
{
}
//Try to close the tcpClient
//尝试关闭连接
try
{
#if !ANDROID
//Throws uncatchable exception in android
//在安卓系统中抛出不可捕捉的异常
tcpClient.Close();
#endif
}
catch (Exception)
{
}
#endif
}
/// <inheritdoc />
protected override double[] SendStreams(StreamTools.StreamSendWrapper[] streamsToSend, double maxSendTimePerKB, long totalBytesToSend)
{
double[] timings = new double[streamsToSend.Length];
Stream sendingStream;
#if WINDOWS_PHONE || NETFX_CORE
sendingStream = socket.OutputStream.AsStreamForWrite();
#else
sendingStream = connectionStream;
#endif
; i<streamsToSend.Length; i++)
{
)
{
//Write each stream
//写入数据流
timings[i] = streamsToSend[i].ThreadSafeStream.CopyTo(sendingStream, streamsToSend[i].Start, streamsToSend[i].Length, NetworkComms.SendBufferSizeBytes, maxSendTimePerKB, MinSendTimeoutMS);
streamsToSend[i].ThreadSafeStream.Dispose();
}
else
timings[i] = ;
}
#if WINDOWS_PHONE || NETFX_CORE
sendingStream.Flush();
#endif
#if !WINDOWS_PHONE && !NETFX_CORE
if (!tcpClient.Connected)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Error("TCPClient is not marked as connected after write to networkStream. Possibly indicates a dropped connection.");
throw new CommunicationException("TCPClient is not marked as connected after write to networkStream. Possibly indicates a dropped connection.");
}
#endif
return timings;
}
}
介绍开源的.net通信框架NetworkComms框架 源码分析(二十三 )TCPConnection的更多相关文章
- 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()) #阅读源码: #左侧工程栏--- ...
- ABP源码分析二十六:核心框架中的一些其他功能
本文是ABP核心项目源码分析的最后一篇,介绍一些前面遗漏的功能 AbpSession AbpSession: 目前这个和CLR的Session没有什么直接的联系.当然可以自定义的去实现IAbpSess ...
- ④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 ...
随机推荐
- ASCIITable: 演示 Arduino 串口输出的进阶功能
原文地址 - https://www.arduino.cc/en/Tutorial/ASCIITable ASCII字符表 本例展示了高级的串口打印功能,通过本功能可以在Arduino软件(IDE)的 ...
- 【OpenCV练习】简单显示图片的代码
今天依照网上的教程尝试了一下最基本的图片显示. 首先想说一下编译时出现的问题,开始时在编译时会出现无法识别cvReleaseImage的情况,是因为没有在配置中包含相应的core的库文件. 加进去就解 ...
- iOS收到Push后播放声音和震动
一.APNS 1.注册 [cpp] [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNoti ...
- LeetCode 374. Guess Number Higher or Lower
We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...
- SQL Server 树形表非循环递归查询
很多人可能想要查询整个树形表关联的内容都会通过循环递归来查...事实上在微软在SQL2005或以上版本就能用别的语法进行查询,下面是示例. --通过子节点查询父节点WITH TREE AS( ...
- Laravel学习笔记(二)Laravel 应用程序的体系结构
在一切环境就绪了,当然就要开始了解框架了. 站在巨人的肩膀上,学东西会事半功倍.我在网上找到一篇好文章,正好可以让我轻松了解Laravel应用程序的体系结构.因此借来直接用了. 该章节内容翻译自< ...
- WinForm DataGridView分页功能
WinForm 里面的DataGridView不像WebForm里面的GridView那样有自带的分页功能,需要自己写代码来实现分页,效果如下图: 分页控件 .CS: 1 using System; ...
- hive导入数据
替换分隔符为\ sed -i 's/\t/\x1/g;s/;/\x1/g' test1.txt gz压缩 gzip -r test1.txt 查看文件 hdfs dfs -ls /hive/wareh ...
- (转载)afxres找不到问题
在试用VS2010时一个问题困扰了我,就是打开c++项目后,rc的dialog进不去,没法拖控件,把我给抓狂的...而且网上大部分说的都是Directions的问题..我的问题明显不是这个问题. 于是 ...
- STL之序列容器deque
首先看看deque的模板声明: template <class T, class Alloc = allocator<T>> // 原本还有个参数BufSize,现在新版本 ...