﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-csolay-随笔分类-NetWork</title><link>http://www.cppblog.com/csolay/category/17964.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 01 Nov 2011 05:41:35 GMT</lastBuildDate><pubDate>Tue, 01 Nov 2011 05:41:35 GMT</pubDate><ttl>60</ttl><item><title>TCP连接的3次握手和4次挥手</title><link>http://www.cppblog.com/csolay/archive/2011/10/22/158859.html</link><dc:creator>csolay</dc:creator><author>csolay</author><pubDate>Fri, 21 Oct 2011 16:50:00 GMT</pubDate><guid>http://www.cppblog.com/csolay/archive/2011/10/22/158859.html</guid><wfw:comment>http://www.cppblog.com/csolay/comments/158859.html</wfw:comment><comments>http://www.cppblog.com/csolay/archive/2011/10/22/158859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/csolay/comments/commentRss/158859.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/csolay/services/trackbacks/158859.html</trackback:ping><description><![CDATA[<span style="font-family: Arial; font-size: 10pt">在TCP/IP协议中，TCP协议提供可靠的连接服务，采用</span><span style="font-family: Arial; color: red; font-size: 10pt">三次握</span><span style="font-family: Arial; color: red; font-size: 10pt">手</span><span style="font-family: Arial; font-size: 10pt">建立一个连接。 </span>
<p><span style="font-family: Arial; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一次握手：建立连接时，客户端发送syn包(syn=j)到服务器，并进入SYN_SEND状态，等待服务器确认；</span></p>
<p><span style="font-family: Arial; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二次握手：服务器收到syn包，必须确认客户的SYN（ack=j+1），同时自己也发送一个SYN包（syn=k），即SYN+ACK包，此时服务器 进入SYN_RECV状态；</span></p>
<p><span style="font-family: Arial; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第三次握手：客户端收到服务器的SYN＋ACK包，向服务器发送确认包ACK(ack=k+1)，此包发送完毕，客户端和服务器进入ESTABLISHED状态，完成三次握手。 完成三次握手，客户端与服务器开始传送数据。<br /><img src="http://www.cppblog.com/images/cppblog_com/csolay/TCP建立连接.jpg" width="463" height="268" alt="" /><br /><br /></span></p>
<p><span style="font-family: Arial"></span></p>
<p><span style="font-family: Arial; color: red; font-size: 10pt">四次挥手</span></p>
<p><span style="font-family: Arial; font-size: 10pt">由于TCP连接是全双工的，因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动，一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭，而另一方执行被动关闭。</span></p>
<p><span style="font-family: Arial; font-size: 10pt">（1）客户端A发送一个FIN，用来关闭客户A到服务器B的数据传送。</span></p>
<p><span style="font-family: Arial; font-size: 10pt">（2）服务器B收到这个FIN，它发回一个ACK，确认序号为收到的序号加1。和SYN一样，一个FIN将占用一个序号。</span></p>
<p><span style="font-family: Arial; font-size: 10pt">（3）服务器B关闭与客户端A的连接，发送一个FIN给客户端A。</span></p>
<p><span style="font-family: Arial; font-size: 10pt; ">（4）客户端A发回ACK报文确认，并将确认序号设置为收到序号加1。<br /></span><span style="font-size: 12px; "><img src="http://www.cppblog.com/images/cppblog_com/csolay/TCP断开连接.jpg" width="549" height="361" alt="" /><br /></span></p>
<p><br /><span style="font-family: Arial; font-size: 10pt"><strong>为什么需要&#8220;三次握手&#8221;</strong></span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family: Arial; font-size: 10pt">在谢希仁著《计算机网络》第四版中讲&#8220;三次握手&#8221;的目的是&#8220;为了防止已失效的连接请求报文段突然又传送到了服务端，因而产生错误&#8221;。在另一部经典的《计算机网络》一书中讲&#8220;三次握手&#8221;的目的是为了解决&#8220;网络中存在延迟的重复分组&#8221;的问题。这两种不用的表述其实阐明的是同一个问题。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family: Arial; font-size: 10pt">谢希仁版《计算机网络》中的例子是这样的，&#8220;已失效的连接请求报文段&#8221;的产生在这样一种情况下：client发出的第一个连接请求报文段并没有丢失，而是在某个网络结点长时间的滞留了，以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后，就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段，同意建立连接。假设不采用&#8220;三次握手&#8221;，那么只要server发出确认，新的连接就建立了。由于现在client并没有发出建立连接的请求，因此不会理睬server的确认，也不会向server发送数据。但server却以为新的运输连接已经建立，并一直等待client发来数据。这样，server的很多资源就白白浪费掉了。采用&#8220;三次握手&#8221;的办法可以防止上述现象发生。例如刚才那种情况，client不会向server的确认发出确认。server由于收不到确认，就知道client并没有要求建立连接。&#8221;<br /></span><br /><strong><span style="font-family: Arial; font-size: 10pt">为什么需要&#8220;四次挥手&#8221;</span><br /></strong><span style="font-family: Arial; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那可能有人会有疑问，在tcp连接握手时为何ACK是和SYN一起发送，这里ACK却没有何FIN一起发送呢。原因是因为tcp是全双工模式，接收到FIN时意味将没有数据再发来，但是还是可以继续发送数据。</span><br /><span style="font-family: Comic Sans MS; color: #0000ff; font-size: 12pt"><br style="font-size: 10pt" /></span><span style="font-family: Comic Sans MS; color: #0000ff; font-size: 10pt">附录：状态详解<br /><img src="http://www.cppblog.com/images/cppblog_com/csolay/TCP状态转换图.png" width="548" height="772" alt="" /><br /></span></p>
<p><span style="color: #000000; font-size: 10pt">&nbsp;</span><span style="font-family: Arial; color: #000000; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLOSED：表示初始状态。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LISTEN：表示服务器端的某个SOCKET处于监听状态，可以接受连接。　　&nbsp;&nbsp; </span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SYN_RCVD：这个状态表示接受到了SYN报文，在正常情况下，这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态，很短暂，基本上用netstat你是很难看到这种状态的，除非你特意写了一个客户端测试程序，故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时，当收到客户端的ACK报文后，它会进入到ESTABLISHED状态。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SYN_SENT： 这个状态与SYN_RCVD相呼应，当客户端SOCKET执行CONNECT连接时，它首先发送SYN报文，因此也随即它会进入到了SYN_SENT状态，并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp; ESTABLISHED：表示连接已经建立</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp; FIN_WAIT_1： 这个状态要好好解释一下，其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是：FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时，它想主动关闭连接，向对方发送了FIN报文，此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后，则进入到FIN_WAIT_2状态，当然在实际的正常情况下，无论对方何种情况下，都应该马上回应ACK报文，所以FIN_WAIT_1状态一般是比较难见到的，而FIN_WAIT_2状态还有时常常可以用netstat看到。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">　　FIN_WAIT_2：上面已经详细解释了这种状态，实际上FIN_WAIT_2状态下的SOCKET，表示半连接，也即有一方要求close连接，但另外还告诉对方，我暂时还有点数据需要传送给你，稍后再关闭连接。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">　　TIME_WAIT： 表示收到了对方的FIN报文，并发送出了ACK报文，就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下，收到了对方同时带FIN标志和ACK标志的报文时，可以直接进入到TIME_WAIT状态，而无须经过FIN_WAIT_2状态。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">　　CLOSING： 这种状态比较特殊，实际情况中应该是很少见，属于一种比较罕见的例外状态。正常情况下，当你发送FIN报文后，按理来说是应该先收到（或同时收到）对方的ACK报文，再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后，并没有收到对方的ACK报文，反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢？其实细想一下，也不难得出结论：那就是如果双方几乎在同时close一个SOCKET的话，那么就出现了双方同时发送FIN报文的情况，也即会出现CLOSING状态，表示双方都正在关闭SOCKET连接。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">　　CLOSE_WAIT： 这种状态的含义其实是表示在等待关闭。怎么理解呢？当对方close一个SOCKET后发送FIN报文给自己，你系统毫无疑问地会回应一个ACK报文给对方，此时则进入到CLOSE_WAIT状态。接下来呢，实际上你真正需要考虑的事情是察看你是否还有数据发送给对方，如果没有的话，那么你也就可以close这个SOCKET，发送FIN报文给对方，也即关闭连接。所以你在CLOSE_WAIT状态下，需要完成的事情是等待你去关闭连接。</span></p>
<p><span style="font-family: Arial; color: #000000; font-size: 10pt">　　LAST_ACK： 这个状态还是比较容易好理解的，它是被动关闭一方在发送FIN报文后，最后等待对方的ACK报文。当收到ACK报文后，也即可以进入到CLOSED可用状态了。</span><span style="font-family: Comic Sans MS; font-size: 12pt"></span></p><img src ="http://www.cppblog.com/csolay/aggbug/158859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/csolay/" target="_blank">csolay</a> 2011-10-22 00:50 <a href="http://www.cppblog.com/csolay/archive/2011/10/22/158859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>