介绍开源的.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 ...
随机推荐
- Repeater控件使用中的一些小问题
网页上用来展示列表的数据,发现还是Repeater比GridView,DetailView之类的要灵活些,所以近期用到了就总结下遇到的一些情况,保留下来以备之后查阅,不用现问度娘了... 自己摸索的, ...
- GHOST(幽灵)重大漏洞
cd /usr/local/srcwget https://webshare.uchicago.edu/orgs/ITServices/itsec/Downloads/GHOST.cgcc GHOST ...
- DataGridView导出到Excel的三个方法
#region DataGridView数据显示到Excel /// <summary> /// 打开Excel并将DataGridView控件中数据导出到Excel /// </s ...
- 剑指Offer:面试题30——最小的k个数(java实现)
问题描述: 输入n个整数,找出其中最小的k个数 思路1: 先排序,再取前k个 时间复杂度O(nlogn) 下面给出快排序的代码(基于下面Partition函数的方法) public void Quic ...
- UML Sequence sample: if-else
if (balance >= amount) { ... } else { ... }
- linux出现user account has expired解决方案
SUSE Linux 用户user1登陆不了,确认密码没错,使用root用户登陆,su - user1 提示密码不对,passwd user1提示帐户过期user account hasexpired ...
- jmeter上传文件搞了一天,才搞定,没高人帮忙效率就是低,赶紧记下来,以备后用
先用谷歌浏览器抓包,抓到的包类似这样: 在jmeter里添加一个http请求,配置好参数,方法,端口,路径等, 勾选 在“同请求一起发送参数”里填写上面抓包的部分数据: 分别对应录入,勾选“编码” 我 ...
- 【转】Java集合类
http://blog.csdn.net/zhangerqing/article/details/8122075 http://android.blog.51cto.com/268543/400557 ...
- 转 LoadRunner 技巧之THML 与 URL两种录制模式分析
Loadrunner的Virtual User Generator 提供人脚本的录制功能,对于初学者来说,这大大的降低了编写脚本的门槛,loadrunner提供两种录制脚本的方式:Html_based ...
- 微软第四题 给定cost能遍历的最大城市数
有向图中N*N矩阵 cost:M, 最多可以遍历的结点个数 例如A可以有0->1->2->0->1 代价:2+2+3+2=9<10 输出4 #include <io ...