山东德州如何网站建设教程,网站推广连接怎么做的,洛阳网站建设培训,做网站长沙目录内容 TCP协议的可靠性 TCP的三次握手 TCP的四次挥手 C#中#xff0c;TCP/IP建立 三次握手和四次挥手常见面试题
在上一篇文章中讲解了TCP/IP的由来以及报文格式#xff0c;详情请见上一篇文章#xff0c;现在接着来讲讲TCP/IP的可靠性以及通过代码的实现。 在TCP首部的…目录内容 TCP协议的可靠性 TCP的三次握手 TCP的四次挥手 C#中TCP/IP建立 三次握手和四次挥手常见面试题
在上一篇文章中讲解了TCP/IP的由来以及报文格式详情请见上一篇文章现在接着来讲讲TCP/IP的可靠性以及通过代码的实现。 在TCP首部的后面是数据部分数据部分是可选的可以有也可以没有。当一个连接建立和一个连接终止时双方交换的报文段仅为TCP的报文首部。假如发送方没有数据要发送也没有使用任何数据的首部来确认收到的数据例如在处理发送数据超时时也会发送不带数据的报文段。 **
TCP协议的可靠性
** 主要靠以下几点来进行保障 一应用数据分割成TCP认为最适合发送的数据块。这部分是通过MSS(最大数据包长度)选项来控制的这种机制被称为协商机制规定了传输的最大数据块长度。但是MSS只能出现在SYN报文段中若服务端不接收客户端的MSS值则MSS长度定为536字节。通常来讲MSS值是越大越好有利于提高网络利用率。 二重传机制。设置定时器等待确认包如果定时器超时还未收到确认包则报文重传。 三对首部和数据进行校验。 四接收端对收到的数据进行排序然后传输给应用层。 五接收端丢弃重复的数据。 六TCP提供流量控制主要是通过滑动窗口来实现流量控制。 经过上一篇文章和以上的讲解TCP协议的数据帧格式就讲解完毕。 **
TCP的三次握手
** 我们都知道TCP在建立连接和断开连接时需要进行三次握手和四次挥手那么我们可以进行思考三次握手和四次挥手分别都做了些什么又是如何进行的呢 三次握手的过程 TCP建立连接时双方需要经过三次握手具体过程如下 1、第一次握手Client端进入SYN_SEND状态发送一个SYN帧来主动打开传输通道该帧的SYN标志位被设置为1同时会带上Client分配好的SN序列号该SN是根据时间产生的一个随机值通常情况下每隔4ms会加1。同时SYN帧还会带一个MSS最大报文段长度可选项的值表示客户端发送出去的最大数据块的长度 2、第二次握手Server端在收到SYN帧以后会进入SYN_RCVD状态同时返回SYNACK帧给Client主要目的在于通知ClientServer端已收到了SYN消息现在需要进行确认。Server端发出的SYNACK帧的ACK标志位被设置为1其确认序号AN值被设置为Client端的SN1SYNACK帧的SYN标志位被设置为1SN值为Server端生成的SN序号SYNACK帧的MSS表示的是Server端的最大数据长度 3、第三次握手Client在收到Server的第二次握手SYNACK确认帧之后首先会将自己的状态从SYN_SENT变为ESTABLISHED表示自己方向的连接通道已经建立成功Client可以发送数据到Server端。Client发送ACK帧到Server端该ACK帧的ACK标志位被设置为1其确认序号AN值被设置为Server端的SN序列号1。另外Client可能会将ACK帧和第一帧要发送的数据合并到一起发送给Server端 4、Server端在收到Client的ACK帧之后会从SYN_RCVD状态进入ESTABLISHED状态至此Server端方向的通道连接建立成功Server端可以发送数据到Client端TCP的全双工连接建立成功。
Client和Server完成了三次握手之后双方就进入了数据传输阶段。数据传输完成后连接将断开连接断开的过程需要经过四次挥手。 **
TCP的四次挥手
** Client端和Server端完成数据通信后TCP连接开始处于断开的过程在这个过程中连接的每个端都能独立地、主动的发起断开的过程中TCP协议使用四次挥手操作。 四次挥手具体过程如下 1、第一次挥手主动断开方客户端或者服务端向对方发送一个FIN结束请求报文此报文的FIN标志位被设置为1并且正确设置Sequence Number序列号和Acknowledgment Number确认号。发送完成后主动断开方进入FIN_WAIT_1状态表示主动断开方没有数据要发送给对方准备关闭连接 2、第二次挥手在收到主动断开方发送的FIN断开请求报文后被动断开方会发送一个ACK响应报文报文的AN值为主动断开请求方报文的SN1该ACK确认报文的含义是我同意你的连接断开请求。然后被动断开方进入CLOSE_WAIT关闭等待状态TCP协议服务会通知高层的应用进程对方向本地方向的连接已经关闭对方已经没有数据可以发送若本地还要发送数据给对方对方依然会接收。被动断开方的CLOSE_WAIT关闭等待还要持续一段时间也就是整个CLOSE_WAIT状态持续的时间。主动断开方在收到ACK报文后由FIN_WAIT_1转换为FIN_WAIT_2状态 3、第三次挥手在发送完ACK报文后被动断开方还可以继续完成数据发送等待剩余数据发送完成后或者CLOSE_WAIT关闭等待截至后被动断开方向主动断开方发送一个FINACK结束响应报文表示被动断开方的数据已发送完毕被动断开方进入LAST_ACK状态 4、第四次挥手主动断开方在收到FINACK断开响应报文后还需要进行最后确认向被动断开方发送一个ACK确认报文然后进入TIME_WAIT状态等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方在等待2MSLTCP报文段在网络中最大的存活时间后如果期间没有收到其它报文则证明对方已正常关闭主动断开方的连接最终关闭。被动断开方在收到主动断开方的最后发送的ACK报文后最终关闭连接。
在第四次挥手中为什么是等待2MSLMaximun Segment Lifetime呢 因为2MSL指的是一个TCP报文段在网络中最大的存活时间2MSL对应于一次消息的来回一个发送和一个回复所需的最大时间。如果超过了2MSL主动断开方都没有收到对方的报文如FIN报文则可以推断ACK已经被对方成功接收此时主动断开方将最终结束自己的TCP连接。所以TCP的TIME_WAIT状态也称为2MSL状态。
通过以上讲解的三次握手和四次挥手一次TCP的连接和断开至少进行7次通信可见其成本是很高的。 **
C#中TCP/IP建立
** Server端
internal class TCPIPServer{private Socket serverSocket null;private int serverPort 3401;#region 用来专门监听等待工作的线程private Thread serverListenThread;#endregionpublic TCPIPServer(){/** AddressFamily.InterNetwork寻找地址的方式此时选定的是IPV4地址* SocketType.Stream数据传输方式此时选择的是Stream传输能够将数据准确的进行传输* ProtocolType.Tcp执行的协议此时选择的是TCP协议*/serverSocket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ipAddress IPAddress.Any; //IPAddress.Parse(GetLocalAddressIP());//绑定IP地址和端口serverSocket.Bind(new IPEndPoint(ipAddress, serverPort));//设置最多15个排队连接请求serverSocket.Listen(15);serverListenThread new Thread(ListenConnect);//关闭后台线程serverListenThread.IsBackground true;serverListenThread.Start();Console.WriteLine(服务端监听中);}private string GetLocalAddressIP(){string localAddressIP string.Empty;foreach(IPAddress ipAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList){if(ipAddress.AddressFamily.ToString() InterNetwork){localAddressIP ipAddress.ToString();}}Console.WriteLine(localAddressIP: localAddressIP);return localAddressIP;}private void ListenConnect(){while (true){try{Socket clientSecket serverSocket.Accept();byte[] buffer Encoding.Default.GetBytes(服务器连接成功);clientSecket.Send(buffer);string client clientSecket.RemoteEndPoint.ToString();Thread clientMsgThread new Thread(ReceiveMessage);clientMsgThread.IsBackground true;clientMsgThread.Start(clientSecket);}catch(Exception ex){serverListenThread.Interrupt();Console.WriteLine(ex.Message);}}}/// summary/// 提取客户端发送的消息/// /summary/// param nameclientSocket/paramprivate void ReceiveMessage(object clientSocket){Socket client clientSocket as Socket;while (true){byte[] readBuffer new byte[1024 * 1024 * 2];int len -1;try{len client.Receive(readBuffer);if(len 0){string endPointMsg client.RemoteEndPoint.ToString();Console.WriteLine(string.Format(endPointMsg: {0}, endPointMsg));break;}else{string clientMsg Encoding.Default.GetString(readBuffer, 0, len);Console.WriteLine(${DateTime.Now}【接收】{clientMsg}{Environment.NewLine});}}catch(Exception ex){string endPointMsg client.RemoteEndPoint.ToString();Console.WriteLine(string.Format(endPointMsg: {0}; Exception:{1}, endPointMsg, ex.Message));break;}}}}运行结果
Client端
internal class TCPIPClient{private Socket clientSocket null;private Thread clientThread null;public TCPIPClient(){clientSocket new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);IPAddress serverIPAddress IPAddress.Parse(GetLocalAddressIP());clientSocket.Connect(new IPEndPoint(serverIPAddress, 3401));byte[] sendMsg Encoding.Default.GetBytes(客户端发送消息啦);clientSocket.Send(sendMsg);clientThread new Thread(ReceiveServerMessage);clientThread.IsBackground true;clientThread.Start();}private void ReceiveServerMessage(){while (true){byte[] readBuffer new byte[1024 * 1024 * 2];int len -1;try{len clientSocket.Receive(readBuffer);if(len 0){string serverMsg Encoding.Default.GetString(readBuffer, 0, len);Console.WriteLine(${DateTime.Now}【接收】{serverMsg}{Environment.NewLine});}else if(len 0){clientThread.Interrupt();Console.WriteLine($Client get data len{len});break;}}catch(Exception ex){Console.WriteLine(ex.Message);break;}}}private string GetLocalAddressIP(){string localAddressIP string.Empty;foreach (IPAddress ipAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList){if (ipAddress.AddressFamily.ToString() InterNetwork){localAddressIP ipAddress.ToString();}}Console.WriteLine(localAddressIP: localAddressIP);return localAddressIP;}}运行结果
**
三次握手和四次挥手常见面试题
** 1为什么关闭连接需要四次挥手而建立连接只需要三次握手 因为在关闭连接时被动断开方在收到主动断开方的FIN结束请求报文时很有可能数据还没有发送完毕不能立即关闭连接被动断开方只能先回复一个ACK响应报文告诉主动断开方“你发送的FIN报文我收到了只有等到我所有的数据都发送完毕后我才能真正的结束在结束之前我会向你发送FINACK报文你先等着”。所以被动断开方的确认报文需要拆成两步故总体就需要四步。 而在建立连接场景中Server端的应答稍微简单一些。当Server端收到Client端的SYN连接请求报文后其ACK报文表示对请求报文的应答SYN报文用来表示服务端的连接已经同步开启而ACK报文和SYN报文之间不会有其它报文需要发送可以将这两者合二为一可以直接发送SYNACK报文。所以在建立连接时只需要三次握手。 2为什么连接建立的时候是三次握手可以改成两次握手码 三次握手完成两个重要的功能一是双方都做好发送数据的准备而且双方都知道对方已经准备好了二是双方完成初始SN序列号的协商双方的SN序列号在握手过程中被发送和确认。 如果把三次握手改成两次握手可能发生死锁。两次握手缺少Client端的二次ACK帧确认假想的TCP建立连接时二次握手可能如下图所示
在假想的TCP建立连接两次握手过程中Client端发送Server端的SYN请求帧Server端收到后发送了确认应答SYNACK帧。按照两次握手的协定Server端认为连接已经建立成功可以开始发送数据帧。这个过程中如果确认应答SYNACK帧在传输过程中丢失Client端没有收到Client端将不知道Server端是否准备好了也不知道Server端的SN序列号Client端认为连接未建立成功将忽略Server端发送的任何分组数据会一直等待Server端SYNACK确认应答帧。在Server端发出数据帧后一直没有收到对应的ACK确认后就会产生超时重复发送同样的数据帧从而陷入死锁。 3为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间 因为主动断开方等待2MSL时间是为了确保主动断开方和被动断开方都能最终关闭。假设网络是不可靠的被动断开方发送FINACK报文后主动断开方的ACK响应报文有可能丢失这时被动断开方处于LASTACK状态由于收不到ACK确认码被动断开方一直不能进入CLOSED状态。在这种情况下被动断开方会超时重传FINACK断开响应报文如果主动断开方在2MSL时间内收到这个重传的FINACK报文会重传一次ACK报文再一次重新启动2MSL计时等待这样就能确保被动断开方收到ACK报文确保被动断开方顺利进入CLOSED状态。只有这样双方都能够确保关闭。反过来如果主动断开方在发送完ACK响应报文后不是进入TIME_WAIT状态去等待2MSL时间而是立即释放连接则将无法收到被动断开方重传的FINACK报文所以不会在发送一次ACK确认报文此时处于LAST_ACK状态的被动断开方无法正常进入到CLOSED状态。 另一方面防止“旧连接的已失效的数据报文”出现在新连接中主动断开方在发送完最后一个ACK报文后在经过2MSL才能最终关闭和释放断开。因此相同端口的TCP新连接需要在2MSL时间之后才能够正常建立连接。2MSL时间内旧连接产生的所有数据报文都已经从网络中消失了从而确保了下一个新连接中不会出现旧连接报文请求。 4如果已经建立了连接但是Client端突然出现故障怎么办 TCP设有一个保活计时器如果Client端出现故障Server端不会一直等待如果一直等待会造成系统资源浪费。Server端每收到一次Client端的数据帧后Server端的保活计时器会复位。计时器的超时设置时间通常为2小时若2小时还没有收到Client端的任何数据帧Server端就会发送一个探测报文段之后每隔75秒发送一次。若连续发送10个探测报文仍没有反应Server端就认为Client出现故障Server端会关闭连接。如果保活计时器的2小时间隔太长可自行调整TCP连接的保活参数。