初识TCP,实验加抓包带你理解为什么需要三次握手、四次挥手

作者:一天 首发公众号:网络之路博客(ID:NetworkBlog)

前言

在前面的第二篇讲过一个通信的流程,里面提到了三种应用,HTTP、DNS、以及DHCP,这些呢,都是属于应用层的应用程序,正式因为越来越多的应用程序的出现,丰富了整个网络世界,对于学习路由交换数通技术来说,应用程序不是重点,特别对于新手来说,了解下常见的协议以及常见端口号的即可。而对于传输层来说,把TCP、UDP的特点、工作流程掌握,有个一定的认知,否则讲解的越多,对初学者来说会吸收不了,犯迷糊,建议是后续在随着知识点深入后,在回过头把TCP/IP协议框架看一次,你会发现又不一样的体会跟收获。

常见的应用程序端口号与作用

应用程序在如今的网络非常非常多,初学者先掌握常见的端口号以及它们的作用,至于原理,后续会讲解几个路由交换上面也常用的,其他应用建议在有一定的路由交换功底后,在花费时间去学习下。

这些应用协议很多在我们生活中经常遇到,经常使用的163、QQ等邮箱,使用的则是SMTP、POP3、IMAP4协议,而浏览器浏览各种网页、视频使用的是HTTP与HTTPS,当在某个应用输入域名(比如baidu.com),则需要好DNS来提供解析成对应的IP,又或者当我们手机连接WIFI后,就能直接上网,则是DHCP为终端分配了IP、DNS等参数。对于这些应用,目前了解下端口号以及作用,会随着知识点的深入,慢慢的都接触到。

竟然应用程序都以不同的端口号来区分,那假设网络中大家的用程序使用了自己觉得好记的端口号,那是不是就可能会造成冲突了呢?所以针对这种情况,端口号的分配有一个专门组织来管理,IANA(互联网号码分配机构),这个组织负责端口号的注册分配情况,如果某一个端口号已经被分配了,那在来注册这个端口号的应用则被拒绝,并且IANA把端口号分为了三类。(IP地址分配也是这个机构)

  • 知名端口(公认端口号),范围是0到1023,像大家一提到80就知道是http、提到20、21就知道是FTP,在使用过程中就有明显的感觉,当我们在浏览器输入网址ccieh3c.com的时候,并没有说去输入ccieh3c.com:80这样的,就是因为这个是知名标准端口号,不用特意告诉浏览器这个端口号是多少,可以了解0到1023主要给互联网上面主流的应用程序进行使用,也有的书上面会写这个范围为保留端口
  • 注册端口与动态端口:端口号范围1024-65535,这部分的范围没有明确给某个特定的对象,不同的应用程序根据自己需要来定义、注册范围,也可以用来做动态端口服务,其实我个人更加习惯把这两个合并成一个,叫做自由端口号(开放端口号)
  • 对于这些概念性的内容,大家知道有这样一个称呼就行,主要明白目前主流应用的端口号都在0~1023之间,记住常见应用的端口号即可。并且一个服务器上面可能运行多个应用程序服务,它可以通过不同的端口号来识别访问者到底访问哪一个服务,比如目的端口号是21,服务器就知道是访问FTP服务,如果是80,则是HTTP,并且绝大部分的应用程序都是基于客户端–服务器的模型,客户端进行请求访问,服务器响应请求。

除了端口号以外,还有一个比较值得关注的就是,在应用协议分类里面还会分为TCP、UDP两种传输层协议,为什么要分两种协议呢,不能都是TCP或者使用UDP完成吗,他们有什么的特点?接下来先进入TCP的世界。

进入TCP的世界

之前学习了IP协议的特性,它是一个无连接,不可靠的协议,这个可能大家不太理解

  • 无连接:比如A跟B在语音通话,告诉他填写一个IP地址,说的是 192.168.2.58,这段语音会转换成数据发送给B,IP在中途传输的时候,可能由于延迟或者路径不一样,导致部分数据是晚到的,B收到后,听到的变成了 192.186.52.8了。这就是IP无连接的意思,每个数据包之间是相互独立、无顺序发送,对端也根据接受的顺序得到结果,但是实际上这些数据包是有关联的。
  • 不可靠:IP协议不能保证数据包能成功的到达目的地,它仅仅提供传输服务,如果中途出现故障丢包,IP协议不会进行任何操作。

正是因为这样的原因,有些应用是必须保证可靠性、跟按顺序接收的,否则得到的结果大不一样,所以TCP/IP的另外一个重要的协议就解决了这个问题,这就是TCP协议。

(1)什么是TCP

先了解下TCP的功能,TCP是面向连接、可靠、基于字节流的传输层通信协议。

  • 面向连接:TCP提供了一对一的连接,比如A与B、C通信,基于TCP协议的话,A会同时跟B、C建立一个一对一的连接。
  • 可靠:在TCP中,无论中间的网络环境是好是坏,都可以保证一个报文一定能够到达对方。
  • 字节流:在TCP中,它不像IP一样,有一个包就发一个,可能等待多一点在一起发送给对方,并且都是有序的,当接收者收到了对应的数据后,但是有其中一个数据没有收到,那么不会丢给应用层去处理,它会等待全部的数据到了后,然后按顺序的交给应用层。

这些特性是不是就是IP协议所不具备的,或者说解决了IP协议的缺陷。

(2)熟悉TCP协议头部格式

熟悉下TCP的头部格式,重点部分会用颜色标识出来,其他字段作为初学者可以跳过。

源目端口号:通常源端口号为随机端口号,访问者随机生成,而目的端口号用于表示访问上层的应用程序具体的端口,比如80就是http,443就是https。

序列号:计算机在发送数据之前,会与对方建立一个TCP的连接,在建立连接的时候会生成一个随机数作为初始值,通过SYN包传给对端,每发送一次数据,就累加一次,作用就是用来解决网络数据包乱序的问题。

确认应答号:表示下一次期望收到的数据的序列号,并且发送端在收到这个确认应答以后,可以根据序列号来判断,这个序号之前的数据已经正常接收,这个字段用于解决网络中丢包等问题。

控制位:控制位总共有6个,主要需要知道的有4个。

  • ACK:确认应答中使用时,该位为1时,TCP规定了除了最初建立的SYN包之外,其余包该为必须置1。
  • RST:该位为1时,表示TCP连接中出现异常必须强制断开。
  • SYN:该位为1时,表示TCP连接建立的开始,并且将序列号字段进行初始化值的设定。
  • FIN:该为为1时,表示后续不在有数据发送了,希望能够断开连接,通知对方结束通信。

字段了解完毕了,上面介绍的就是需要了解的,但是光看字段是不是很抽象,下面就来实际体会下TCP的建立。

(3)TCP的是如何建立的

TCP是面向连接的协议,在正式使用TCP之前,必须先建立一个连接,TCP建立连接通过三次握手来实现

按图设置好地址、开启服务,开启抓包(记得启动设备),中间必须加一个交换机,否则无法抓包。

通过HTTP客户端访问192.168.255.2服务器提供的WEB访问

主要放在前面三个包,这个就是TCP建立的三次握手。

  • TCP三次握手第一个包:SYN

最开始,客户端与服务器都是出于CLOSED状态,服务器由于跑了WEB服务,会主动去监听对应的端口号,变成LSTEN状态。客户端想要访问服务器,会随机初始化序列号(Client_isn,这里为6509),把这个序列号放入头部的序列号字段中,同时把SYN标志变成1,用于表示这个是SYN报文,然后发送给服务器,告诉服务器,我要跟你建立连接,发送完成后,客户端状态变成SYN-SENT。

  • TCP三次握手第二个包:SYN+ACK

服务器收到客户端的SYN后,首先服务器也生成一个随机的序号(Server_isn,这里为7773),这个序号填入TCP头部的序列号字段中,另外确认应答号填入(Client_isn+1,6509+1),最后把控制位的SYN与ACK标志变成1后发送给客户端。

  • TCP三层握手第三个包:ACK

客户端收到服务器的报文后,还需要发送最后一个应答报文,于是将TCP头部的ACK标志位变成1,紧接着确认应答号字段填写Server_isn+1(7773+1),最后把这个报文送给服务器,发送出去后,客户端的状态变成ESTBLISHED。

服务器收到客户端的应答报文后,也进入ESTABLISHED状态,至此双方都进入ESTABLISHED状态,三次握手完成,连接已经建立,客户端与服务器就可以相互发送数据了。

  • 能得到有用知识点信息汇总
  • 客户端访问服务器应用的时候,源端口号通常为随机端口号,目的端口号为知名应用的端口(比如http为80)
  • TCP应用在发送数据之前必须是建立TCP连接(三次握手)
  • TCP建立首包(第一个包)叫做SYN,客户端会随机生成一个序列号(抓包里面有两个值,一个是相对序列号,这个是抓包软件为了你查看方便生成的,真实的序列号就是随机的那个值)填入序列号字段,并且把SYN位变成1,发送给服务器。
  • TCP建立第二个包叫做SYN+ACK,属于服务端处理,ACK用于回应客户端的SYN(在客户端SYN序列号上+1),把结果写入确认应答号字段,同时服务端也会生成自己的SYN,随机生成一个序列号填入序列号字段中,同时把SYN、ACK位变成1,然后发送给客户端。
  • TCP建立第三个包叫做ACK,客户端收到后,用ACK回应服务端的SYN(在服务器的SYN序列号上+1),把ACK位变成1,把这个回应发送给服务端,这个客户端这边就已经完成建立,状态变成Established,服务器在收到以后,也会变成Establilied状态,至此两边建立完成,可以正常开始发送数据。
  • 这样TCP就建立了一个一对一的连接了(客户端与服务器双向连接),比如这个客户端要访问不同的服务器,那就需要跟不同的服务器建立多个TCP的连接。

(2)TCP为什么一定要三次握手,目的是什么?

  • 刚开始的时候客户端与服务端都处于关闭状态,各自都不能确定对方是否能够接收、发送正常,但是客户端需要访问服务端的内容资源,这时候客户端发SYN的作用产生随机序列号,并且通知服务端,我要跟你建立连接了,客户端的状态变成SYN_SENT。

  • 服务端收到客户端的SYN后,它可以确认自己接收正常的,对方发送正常,但是无法确认的是自己发送是否正常,对方接收是否正常,于是服务器也发送一个SYN包,随机产生一个序列号,告诉客户端,我也要跟你建立链接,同时ACK响应客户端的SYN请求(在客户端SYN生成的序列号上+1),告诉客户端我已经成功接收,此时状态由Lisen变成SYS_RCVD。

  • 客户端收到来自服务器的SYN+ACK后,可以确认,自己的接收没问题(收到了服务端的SYN),并且发送也没问题(收到了服务器的ACK确认),怎么辨别这个ACK就是确认的呢(看ACK里面的值,是不是之前的序列号+1),但是此时服务端还不知道自己发送的内容有没有被收到,所以客户端这边还需要回复一个ACK告知服务器(以服务器SYN中的序列号值+1)。

  • 最终完成连接建立,这个都是我们熟悉的,但是从上面的步骤来看是不是又清晰了很多,回到主题,三次握手的目的是什么呢?
    • 三次握手的目的是为了建立可靠的通信连接,而通信简单来说就是数据的发送与接收,三次握手最根本的目的就是确认双方的发送与接收能力是否正常。(这三个包能够来确认双方是否接收与发送都正常)
    • 双方都知道了彼此已经做好了发送数据的准备(因为TCP是全双工的,客户端与服务器建立了连接,服务器也同时与客户端建立连接,这也是为什么服务器要发送SYN的原因)
    • 双方都生成初始化序列号,在握手过程中被发送和确认(有了这个序列号,双方可以在彼此的序列号上+1的方式来确认回复对方)

  • 有的面试会提到为什么是三次握手,而不是两次呢?

通过上面的图就可以看出来,虽然第二个包服务端也就回复了SYN与ACK,但是服务端并不能确认这个包是否抵达了客户端,假设只有两次握手,当客户端的SYN请求发送出去了,但是由于网络问题被丢弃或者服务器回应的ACK没有收到,会重新发送SYN,由于没有第三次的握手存在,服务端不清楚客户端是否收到自己的ACK确认了,导致的情况是每收到一个SYN就建立一个连接,这样会导致服务端建立多个无效的连接,占用了设备的资源,更容易被恶意攻击。

(3)简单看看,TCP如何保障应用的可靠性的

来看看红色标记中的三个包,一个是客户端请求的HTTP数据,一个是服务端响应的,加一个ACK。

为了方便理解,我们就用抓包软件给的相对值来看,这样看起来清晰些,随机值可能一眼看过去有点迷糊,需要计算。

  • 客户端的相对序列号为1,使用三次握手后的序列号(0+1),这样的序列号是随机生成的,可以有效的低于恶意攻击者来伪装。但是软件里面显示了一个Next Sequence Number为160,软件怎么知道下一个序列号就是从160呢,待会看服务端就明白了。
  • ACK为1,在建立三次握手的时候,每发送一个包会加1,这里为HTTP的请求包,也是三次握手后的首包,服务器并没有发送其他包过来,所以ACK的值还是1。
  • TCP Payload,为实际应用层的数据,这里就是客户端的HTTP请求数据。

  • 服务器的相对序列号为1,也是三次握手后最后的那个值,对应上面客户端的ACK
  • ACK的相对值是160,160怎么来的呢?本身对方的序列号是1,然后加上TCP Payload(实际数据)是159,所以这里值是(1+159)最终是160(真正的随机6510+159=6669),用于告诉客户端我已经成功接收了。
  • TCP Paylod:应用层数据,这里就服务器的HTTP回应。

最后客户端收到服务器的响应后,回复ACK,表示收到了。

  • 可以看出来TCP可靠性离不开序列号与ACK
    • 客户端与服务器收到对方发送的数据后,用ACK来进行确认(ACK的值序列号+实际数据),告诉对方已经正常接收。
    • 接收方可以根据数据包的序列号来按序接收排列。(可能客户端连续发了3个包,服务端收到以后,可以通过序列号的值来排列)
    • 客户端与服务器的序列号都是随机生成,不相同(软件做了优化的相对值都是从0开始,所以一样)真正的随机值是不一样的,这样可以防止某些时旧的连接与新的连接序列号一样,造成数据混乱,另外还可以避免攻击者伪装相同的序列号。
    • 如果客户端与服务器的数据发送出去,在一定时间内没有收到对方的ACK回复,及可能这个数据对方没有收到,TCP会进行重传,保证可靠性。
    • 如果客户端与服务器收到数据后,回复了ACK,可能网络问题ACK丢失了,导致对方又重发了数据,接收方可以根据序列号去重(判断出重复的内容),直接再次发送ACK表示已经收到了。

(4)TCP又是如何断开的呢?

如果客户端与服务器的数据已经发送完毕,一段时间内不在发送数据了,TCP建立的连接通道会占用服务器的性能,所以TCP规定,当应用服务不在有数据传输的需求时,应该断开连接,TCP断开的方式是通过四次挥手。

  • 从抓包的info里面其实就可以看出来,最后四个就是四次挥手,因为包含了FIN的标志,上面介绍过包含这个值的时候是表示请求断开,已经没有数据发送了。(这个就不在抓包一个一个去看了,了解下过程)
  • 客户端由于请求已经得到响应,没有数据发送,打算关闭连接,会发送一个TCP标志位FIN为1的FIN报文,客户端进入FIN_WAIT_1状态(细心的你可以发现ACK位也变成1了,但这这个包的ACK位没什么作用,可以发现很多书里面,直接把ACK都省去了,只提FIN)。
  • 服务端收到该报文后,就向客户端发送ACK应答报文,表示知道了,接着服务端进入CLOSED_WAIT状态
  • 客户端收到服务端的ACK后,进入FIN_WAIT_2状态。
  • 服务端如果这时候有数据处理,那么客户端会等待,处理完毕后,服务端会向客户端发送FIN报文,之后也进入LAST_ACK状态
  • 客户端收到FIN后,回应ACK,之后进入TIME_WAIT状态
  • 服务端收到ACK应答后,就进入CLOSE状态,这时候服务端到客户端的连接关闭
  • 客户端在经过2MSL时间后,自动进入CLOSED状态,这时候客户端到服务端的连接关闭。
  • 注意的是,主动发起关闭连接的,才会有TIME_WAIT状态(实际中不一定是客户端先发起哦)

  • 为什么挥手需要四次呢?

其实仔细看了上面的过程,就理解为什么需要四次了。

  • 关闭连接时,客户端给服务器发送FIN,只代表客户端不在发送数据了,但是它还能接收来自于服务器的数据的(可能服务器发送过来的数据没有完毕)。
  • 服务器收到客户端的FIN报文时,发送一个ACK应答报文,服务器可能还有数据没处理完毕,需要在没有数据发送后,才发送FIN报文给客户端表示我也没有数据发送了,关闭连接。
  • 通常情况下,服务端都会等待数据发送和处理完成,所以服务端的ACK和FIN通常情况下会分开发送,从而比三次握手要多了一次。

TCP的option MSS

  • MSS的作用

  • MTU我们之前了解过,是以太网数据部分1500个字节
  • MSS则是1500-IP和TCP头部后,实际数据的最大的容纳长度。TCP MSS的出现就是为了避免数据包的大小超过MTU,导致在网络层分片,因为网络层的分片是最没有效率的,比如分片中的某一个丢失,那么整个IP数据包所有的分片都需要重传。(回顾:某一片丢失需要都重传是因为在分片中除了第一片会携带上层头部以外,其余分片是不携带的)那么MSS怎么避免呢?

在TCP三次握手建立的时候,通常是会协商双方的MSS值的,当TCP发现数据超过MSS时,就会先进行分片,保证在打上TCP头部,以及IP头部的长度后不大于MTU值,这样来避免IP分片。

TCP分片后,如果某一个分片丢失,TCP会重发丢失部分的MSS,而不用重传所有分片,这样大大的提高了效率。(补充:MSS分片是都具有TCP头部,所以可以针对某一片重传。因为发送方没有收到对方对应部分的ACK应答,会进行重传,这就是TCP层的可靠性保障跟数据交付。)

最后补充下,TCP中的数据称为段,就跟IP层为包,数据链路层为帧一样,但是在实际交流中,更多会称呼为数据包。

(6)怎么才算一个TCP连接呢?

  • 上面介绍了不同应用的端口号,通常情况下都会觉得通过源目端口号来确定一个连接,其实不是这样的。

有没有这样的可能,端口号最大只有65535,可能在某个时刻,同时访问服务器中就有相同的源端口号,因为目的端口号是服务器提供的服务端口号,都是相同的,这个时候服务器收到以后没法确认一个唯一的TCP连接,因为端口号都一样,所以确定唯一TCP连接是包含四元组:源地址、目的地址、源端口号、目的端口号

  • 源地址和⽬的地址:作⽤是通过 IP 协议发送报⽂给对⽅主机。
  • 源端⼝和⽬的端⼝:作⽤是告诉 TCP 协议应该把报⽂发给哪个进程。

Windows命令行学习

在Windows中可以通过netstat开查看当前PC或者服务器的TCP网络连接情况

可以通过/?看帮助命令

可以查看本机就有这么多在侦听的端口号,为什么会侦听呢?就是因为开了对应的服务,就会侦听,当有数据包过来访问的是某某端口号,就交给对应侦听端口号的上层应用处理。

抓包的小技巧

抓包中软件会生成一个相对随机值,方便我们查看,但是新手朋友很容易把这个值当成了TCP序列号本来就是这个样子的,更糟糕的可能认为客户端与服务器的序列号是一样的,所以可以在抓包中关闭这个选项

右击,协议首选项—-transmission Control protocl—-红色框框的√去掉

这个时候看,序列号就成了随机的了

流类型选择【TCP Flod】就可以只看TCP的了,能够完整的看到整个TCP的状态以及序列号与ACK的交互过程,建议save as导出看起来更加清晰。

留两个小疑问

  • 这里看抓包里面协商的MSS为什么是1460呢?
  • TCP协议在IP中的Protocol字段中ID为多少

参考文献

[1]图解网络-小林coding-第三版

“承上启下”

TCP在这里就告一段落了,本身这个协议非常非常的复杂,这里介绍的是对于初学者来说比较有用的知识点,但是只是TCP协议中的冰山一角,而且博主可能要啰嗦几句,对于刚入门特别是想学习路由交换的朋友来说,TCP目前掌握这个程度就可以了,更多的可能需要你有更广泛的知识框架才能够去理解,如果只是单纯的看TCP的各种字段以及参数,你会觉得非常的枯燥,而且过不了多久就忘记了,在这里呢,需要了解的就是TCP为什么需要三次握手以及怎么确定一个TCP连接、四次挥手、MSS的概念即可,更深入的等有一点的功底了,在回过来看,你会发现容易理解很多了,包括看你之前看过的TCP/IP协议体系中的内容,都会有不一样的收获。,下一篇进入对比与TCP来说,简单很多的UDP协议,来看看另外一个传输层协议有什么样的特点。

作者:一天,公众号:网络之路博客(ID:NetworkBlog)。让你的网络之路不在孤单,一起学习,一起成长。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注