显示器参数教学(垫显示器的大学教材)(1)

今天无意间注意到垫显示器的《TCP/IP协议族》,书皮最上面一行字:世界著名计算机教材精选。这是我大学的教材,我的专业是网络工程,大学没能好好学习,惭愧,惭愧。打开随便翻了一下, 发现很多折角,应该是学期末老师划重点的时候折的,唉,回忆袭来。看到这本书,想到的就是之前几次面试的一个高频问题:tcp三次握手和四次挥手。赶紧翻到运输层TCP那一节。映入眼帘的是我没交的作业。。。

显示器参数教学(垫显示器的大学教材)(2)

万千思绪啊,怀念啊。。

TCP

TCP使用端口号提供进程到进程的通信。tcp和UDP都是属于运输层。tcp使用的我们常见的端口号有FTP(20/21)TELNET(23)SMTP(25)DNS(53)HTTP(80)。和UDP不同,TCP是一种面向流的协议。所谓面向流,我理解的就是:UDP报文之间没有没有任何关联,而TCP则允许发送进程以字节流的形式来传送数据,并且也允许接受进程把数据作为字节流来接收,也就是是说建立了一条连接,这条连接就像一个管道,以此进行全双工的通信。

显示器参数教学(垫显示器的大学教材)(3)

由于发送进程和接收进程写入和读取数据的速度可能不同,TCP会用缓存来存储数据。所以TCP有两个缓存,即发送缓存和接收缓存。这些缓存还被TCP用来进行流量控制和差错控制。

TCP是一个可靠的运输协议,它使用确认机制来检查数据是否安全完好地到达。

TCP报文的首部有很多字段,包括源端口地址、目的端口地址、序号、确认号、首部长度、保留、控制、窗口大小、检验和、紧急指针、选项。其中控制字段定义了6中不同的控制位或标志:

TCP连接

TCP是面向连接的,需要经历三个阶段:建立连接、数据传输、连接终止

建立连接

TCP建立连接的过程成为三向握手(three-way handshaking)

显示器参数教学(垫显示器的大学教材)(4)

过程:

准备条件:服务器程序告诉服务器的TCP自己已经准备好接收连接。这个请求被称为被动打开请求。这个打开是针对连接的,而不是端口。这一步就是我们的服务启动了,开放了端口,等待连接。

  1. 客户端程序发出请求,称为主动打开。客户端发送第一个报文段(SYN报文段),这个报文中只有SYN标志被置为1,并且客户端选择了一个随机数作为序号,并把这个序号发送给服务端。这里同时是客户端在同步它的初始序号。
  2. 服务器发送第二个报文,即SYN ACK报文段,其中两个标志(SYN和ACK)置为1。这个报文有两个目的。首先,他是另一个方向上的SYN报文段,服务器使用这个报文来同步自己的初始序号。其次,服务器还通过ACK标志来确认已收到来自客户端的SYN报文段,同时给出期望从客户端收到的下一个序号。
  3. 客户端发送第三个报文。这仅仅是一个ACK报文段。它使用ACK标志和确认号字段来确认收到了第二个报文。这个报文的序号和SYN报文段使用的序号是一样的,这样可以节省一个序号。当然在某些实现中,这个报文可以携带客户端的第一个数据块,这种情况下,第三个报文必须有一个新的序号来表示数据中的第一个字节编号。但是通常第三个报文段是不包含数据的,因而不消耗序号。

SYN洪泛攻击:了解了上面的连接过程,很多人会想到,如果我向一个服务端发送大量的SYN报文段,是不是可以把连接占满,导致服务不可用?当然,之前我们公司的一些服务使用socket长连接,这些连接通过心跳保持连接,但是由于存在bug,客户端在关闭连接的时候不会发送确认,导致服务端的连接状态一直是close wait,无法真正关闭连接。而客户端却认为这些连接已经断开了,再次申请新的连接,导致长时间不重启服务的话,连接就会被占满。这个问题有一段时间一直困扰着我们,不得不定期重启服务端才能保证业务。这样的机制也让一些不怀好意的人所利用,伪造大量假源IP地址的SYN报文段发送给服务端。服务端认为这是客户端发来的主动打开请求,于是分配必要的资源,并发送SYN ACK报文段给对应的源IP,奈何这些IP都是假的,这些报文终将超时,服务端却已经分配了资源。大量的资源被占用却未被利用,服务器最终会因为资源耗尽而不能接收合法客户的连接请求。这就是SYN洪泛攻击。当然也有一些解决的办法,常见的是使用Cookie,做到推迟资源的分配,直到服务器能够正式连接请求来自合法的IP地址。

数据传输

按照建立连接时双方已经确定的序号发送数据。

举例子得有背景!背景:

显示器参数教学(垫显示器的大学教材)(5)

  1. 客户端推送数据,seq为8001(序号排着来,建立连接用的是8000),ack:15001,携带1000字节数据。控制方面,PSH(推送)标志为1,代表推送数据。
  2. 服务端收到seq为8001的报文,它了解到PSH标志为1,就会尽快把这些数据交给服务端对应端口上的程序。如果由于资源占用等原因不能及时把数据交给程序,TCP有缓存机制,可以先缓存下来,回头再给程序。这里服务器可以直接发送确认报文,也可能因为忙碌没能及时发送确认,但是服务端会在超时时间内发送确认。我们这里假定服务端没立即返回确认,而是收到了客户端发来的第二个数据块。
  3. 客户端发送第二个报文块,seq为8001 1000=9001,序号就是数据的offset嘛,很好理解。ack还是15001,因为服务端没来新的报文嘛。PSH标志还是1,因为还是推送数据。
  4. 服务端这时候收到两个报文,客户端的数据也都传输完了。服务端终于有时间给客户端确认了。这时候它直接发了一个报文,seq为15001,各自按各自的序号来,必定这是告诉对方自己数据的offset,ack为8001 1000 1000=10001,要发确认就一起确认了,没必要每个来自客户端的都得单独确认,记住,序号就是数据的offset。服务端在确认的同时可能还会返回一些数据,就像上面我说的那个socket接口的例子,这时候就会携带接口的响应数据。比如携带了2000字节的数据。这个报文PSH是不是1,不同的TCP实现不一样。
  5. 服务端收到报文,已经传输完所有的数据,就不需要携带数据了,但是得确认收到服务端的报文,seq为10000,不携带数据,offset还是之前的10000,ack为15001 2000=17001。

连接终止

参与连接的任何一方都可以关闭连接,一般都是客户端发起。

显示器参数教学(垫显示器的大学教材)(6)

  1. 客户进程告诉自己的TCP关闭连接,TCP发送第一个报文,这个报文把FIN位置1,成为FIN报文段。这个FIN报文段,可以是最后一个数据块,也可以只是一个控制报文段。不携带数据的话,只消耗一个序号。seq:x,ack:y
  2. 服务器TCP在收到合格FIN报文段后,把这种情况告诉它的进程,并发送第二个报文--FIN ACK报文段,确定客户端的FIN报文,同时也宣布另一个方向正在关闭连接。当然这个报文也可能带着来自服务端的最后一个数据块。如果不携带数据,只消耗一个序号。seq:y,ack:x 1
  3. 客户端TCP发送最后一个报文段,这是一个ACK报文段,确认服务端的FIN报文段。seq:x,ack:y 1
,