TCP协议

1.因果

  出现TCP的原因:IP(网络层)有非常强的不稳定性,网络层跟硬件联系比较紧密,硬件会对文件的传输造成比较大的影响。
  解决方案:传输层会针对于网络层出现的问题进行弥补。有两种方法:
  1)完全不弥补,即能接受到数据报就接受,接收不到就接收不到。这种协议为UDP,称为无连接不可靠的报文传输;
  2)完全弥补,这种协议为TCP,称为面向连接的可靠的数据报传递。

2.TCP通讯时序

在这里插入图片描述

2.1.TCP三次握手(establish connection)

  客户端和服务器建立连接的过程称为三次握手。
  1)客户端发送一个带有SYN标志的TCP报文到服务器,请求连接。

   客户端发出段1,SYN表示连接请求,序号是1000,这个序号在网络通讯中用作临时地址,每发送n个字节的数据,这个序号要加n,这样在接收端可以根据序号排出数据报的正确顺序,也可以发现丢包的情况。另外,规定,SYN位和FIN位也要占1个字节的空间,这次虽然没有发送数据,但是由于发送了SYN位,因此下次再发送应该用序号1001。
  mss表示最大段尺寸,如果一个段太大,封装成帧后超过了链路层的最大帧长度,就必须在IP层分片,为了避免这种情况,客户端声明自己的最大段尺寸,建立服务器端发来的段不要超过这个长度。

  备注:SYN1000(0)中的0表示发送的数据包为0字节。

2)服务器收到请求后,回应客户端。此报文段带ACK标志和SYN标志。它表示对刚才客户端SYN的回应;同时又发送SYN给客户端,询问客户端是否准备好进行数据通讯。

  服务器发出的回应报文段2,也有SYN标志位,同时置ACK位表示确认,确认号为1001,表示“我接收到序号1000及其以前所有的段,请你下次发送序号为1001的段”,也就是应答了客户端的连接请求,同时也给客户段发出一个连接请求,同时声明最大尺寸为1024。

3)客户端必须再次回应服务器一个ACK报文,以表示确认连接。

  客户端发出段3,对服务器的连接请求进行应答,确认序号是8001.在这个过程中,客户端和服务器分别给对方发了连接请求,也应答了对方的连接请求,其中服务器的请求和应答在一个段中发出,因此一共有三个段用于建立连接,称为“三次握手(three-way-handshake)”。在建立连接的同时,双方写上了一些信息,例如双方发送序号的初始值、最大段尺寸等。

  备注:三次握手仅仅是为了建立连接,而没有传送数据。

2.2.数据传输(data transfer)

   1)客户端发出段4,包含从序号1001开始的20个字节数据,同时还要携带上确认好

   2)服务器发出段5,确认序号为1021,对序号为1001-1020的数据表示确认收到,同时请求发送序号1021开始的数据,服务器在应答的同时也向客户端发送从序号8001开始的10个字节数据,这个成为piggyback。

   3)客户端发出段6,对服务发来的序号为8001-8010的数据表示确认收到,请求发送序号8011开始的数据。
   在数据传输过程中,ACK和确认序号是非常重要的,应用程序交给TCP协议发送的数据会暂存在TCP层的发送缓冲区中,发出数据包给对方之后,只有收到对方应答的ACK段才知道该数据包确实发到了对方,可以从发送缓冲区中释放掉了如果因为网络故障丢失了数据包或者丢失了对方发回的ACK段,经过等待超时后TCP协议自动将发送缓冲区中的数据包重发。需要ACK确认序号的原因
   换句话说:客户端在给服务器发送数据的过程中,服务器可以不立即给客户端回复确认号(ACK),可以是客户端发送很多数据之后,服务器才给客户端回复。这时候客户端会根据服务器回复的ACK,判断服务器是否收到了自己发送的所有数据,如果没有接收到所有数据,则重新发送没有接收到的数据。

2.3.关闭连接(close connection)

   客户端和服务器关闭连接的过程称为四次挥手。
   由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
   1)客户端发送段7,FIN为表示关闭连接请求。(FIN区别于SYN)
   2)服务器发出段8,应答客户端的关闭连接请求。
   3)服务器发出段9,其中也包含FIN位,向客户端发送关闭连接请求。
   4)客户端发送段10,应答服务器的关闭连接请求。

   建立的过程是三方握手,而关闭连接通常需要4个段,服务器的应答和关闭连接请求通常不合并一个段中,因为有连接半关闭的情况,这种情况下客户端关闭连接之后就不能再发送数据给服务器了,但是这个服务器还可以发送数据给客户端,直到服务器也关闭连接为止。(这为什么需要三次挥手,而不是四次挥手

3.协议上限分析

   以太网帧最大长度:1500字节

   IP层最大长度:2的16次方=65535字节

   虽然IP层协议允许的最大字节数大于以太网帧协议允许的最大长度,但是由于IP层上的协议数据最后都必须封装在以太网帧里面,所以要进行拆包操作,将大于1500字节的数据报拆分成小于等于1500的数据报,这样以太网帧协议就可以正常封装数据进行传输了。

  这样做的好处:数据包在网络中传输时,由于受到网络环境的影响,可能发生丢包问题。TCP是面向连接的可靠的,基于字节流的传输层通信协议,因此,当发现有丢包时(根据ACK的序号判断是否丢包),会进行重新发送丢包的数据。如果没有协议上限存在,TCP每次传递的数据包较大,这样会导致在每次丢包重发时,多次发送大的数据包,从而拉慢网络通讯,浪费网络资源。

7.滑动窗口(TCP流量控制)

   介绍UDP时,我们描述了这样的问题:如果发送端发送的速度较快,接收端收到数据后处理的速度较慢,而接受缓冲区的大小是固定的,就会丢失数据。TCP协议通过“滑动窗口(Sliding Window)”机制解决这一问题。通讯过程如下图:
在这里插入图片描述
   1)发送端发起连接,声明最大段尺寸是1460,初始序号是0,窗口大小是4K,表示“我的接收缓冲区还有4K字节空闲,你发的数据不要超过4K”。接收端应答连接请求,声明最大段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。
   2)发送端发出段4-9,每个段带1K的数据,发送端根据窗口大小知道接收端的缓冲区满了,因此停止发送数据。
   3)接收端的应用程序提走2K数据,接收缓冲区又有了2K空闲,接收端发出段10,在应答已收到6K数据的同时声明窗口大小为2K。
   4)接收端的应用程序又提走2K数据,接收缓冲区有4K空闲,接收端发出段11,重新声明窗口大小为4K。
   5)发送端发出段12-13,每个段带2K数据,段13同时还包含FIN位。
   6)接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答序号是8194,连接处于半关闭状态,接收端同时声明窗口大小为2K。
   7)接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。
   8)接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为6K。
   9)接收端的应用程序在提走全部数据后,决定关闭连接,发出段17包含FIN位,发送端应答,连接完全关闭。
   上图在接收端用小方块表示1K数据,实心的小方块表示已接收到的数据,虚线框表示接收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序提走数据,虚线框是向右滑动的,因此称为滑动窗口。
   从这个例子还可以看出,发送端是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据。也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),在底层通讯中这些数据可能被拆成很多数据包来发送,但是一个数据包有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

8.TCP状态转换图

在这里插入图片描述
   CLOSED:表示初始状态。
   LISTEN:该状态表示服务器端的某个SOCKET处于监听状态,可以接受连接。
   SYN_SENT:这个状态与SYN_RCVD遥相呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,随即进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
   SYN_RCVD: 该状态表示接收到SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂。此种状态时,当收到客户端的ACK报文后,会进入到ESTABLISHED状态。
   ESTABLISHED:表示连接已经建立。
   FIN_WAIT_1: FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。区别是:
   FIN_WAIT_1状态是当socket在ESTABLISHED状态时,想主动关闭连接,向对方发送了FIN报文,此时该socket进入到FIN_WAIT_1状态。
   FIN_WAIT_2状态是当对方回应ACK后,该socket进入到FIN_WAIT_2状态,正常情况下,对方应马上回应ACK报文,所以FIN_WAIT_1状态一般较难见到,而FIN_WAIT_2状态可用netstat看到。
   FIN_WAIT_2:主动关闭链接的一方,发出FIN收到ACK以后进入该状态。称之为半连接或半关闭状态。该状态下的socket只能接收数据,不能发。
   TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,等2MSL后即可回到   CLOSED可用状态。如果FIN_WAIT_1状态下,收到对方同时带 FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
   CLOSING: 这种状态较特殊,属于一种较罕见的状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是   CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
   CLOSE_WAIT: 此种状态表示在等待关闭。当对方关闭一个SOCKET后发送FIN报文给自己,系统会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,察看是否还有数据发送给对方,如果没有可以 close这个SOCKET,发送FIN报文给对方,即关闭连接。所以在   CLOSE_WAIT状态下,需要关闭连接。
   LAST_ACK: 该状态是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,即可以进入到CLOSED可用状态。

Note:若果程序运行过程中出现问题,可以利用 netstat -apn | grep 6666(端口号)来查询数据连接或数据传输到了哪一步。
在这里插入图片描述

9.半关闭

有如下情况:
  文件描述符都存放在文件描述符表中,通常表中的各文件描述符都指向各自的一个socket,但是也存在两个文件描述符指向一个socket。这时,调用close()函数去关闭套接字,就不能真正的关闭掉。
  此时就需要使用shutdown()。在这里插入图片描述
  使用close中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有当描述符的引用计数为0时才关闭连接。
  shutdown不考虑描述符的引用计数,直接关闭描述符。也可选择中止一个方向的连接,只中止读或只中止写。
注意:

  1. 如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。
  2. 在多进程中如果一个进程调用了shutdown(sfd, SHUT_RDWR)后,其它的进程将无法进行通信。但,如果一个进程close(sfd)将不会影响到其它进程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值