linux高性能服务器编程 (三) --TCP协议详解
第三章 IP协议详解
TCP协议是TCP/IP协议族中的另外一个重要的协议,与IP协议相比,TCP协议更高进应用层。一些重要的socket选项都和TCP协议相关。这一章主要从如下方面学习:
1)TCP头部信息:每一个TCP头部会出现在每一个TCP报文段中
2)TCP状态转移过程:TCP连接的端到端都存在一个状态,从连接到断开都会经历一些状态变迁
3)TCP数据流:TCP数据是基于流的(交互数据流、成块数据流)
4)TCP数据流的控制:TCP的可靠性体现出(超时重传、拥塞控制)
1、TCP和UDP的特定区别
传输层协议主要要两个TCP和UDP协议,但是它们俩是不同类型的两个协议。TCP协议是面向连接的、字节流和可靠传输。所以TCP协议通信必须双方需要进行一个连接且各自为此连接贡献一些内核资源,用于数据传输和写。TCP连接是全双工的也就是双方的数据读写可以通过一个连接进行。TCP协议的连接是一对一的,所以在断开连接的时候,双方都要断开,并且释放双方的内核资源。所以对于广播和多播(目标是多个主机地址)就不能使用TCP协议了。TCP传输是可靠的,因为TCP协议采用了应答发送和超时重发机制。用于确保TCP传递数据是可靠的。TCP协议传递是基于流的,所以它的大小不受限制。
举个例子,接收端收到一个或多个TCP报文段后,TCP模块将会按照TCP报文段的序号进行排序将其放到接收缓冲区,再通知应用程序读取数据。由于字节流的关系,发送端执行的读写操作次数和接收端的读操作次数之间没有数量的关系。应用程序对于发送和接收是没有边界限制的。
而UDP恰恰和TCP相反行成一个互补的作用,两者最重要的区别是TCP是基于字节流的服务,而UPD是基于数据报的服务。数据报的服务就是通信双方必须执行同样次数的读写操作。发送端应用程序每执行一次写操作,UDP模块就将其封装成一个UDP数据报并发送过去,接收端也需要对每换一个数据报执行读操作,否则就会丢包。
2、TCP头部结构
上面说过TCP头部信息会出现在每一个TCP报文段中。主要是记录源端端口和目的端端口以及TCP连接的状态和控制流信息等。
从上图上可了解,16位源端口号和16位目的地端口号,是用来记录请求端和目的地端来自哪里将要去往哪里,客户端的端口号是临时端口号,而目的地端口号是一个知名服务端口号。32位序号是一次TCP通信过程中某一个传输方向上的字节流的每一个字节编号。就好比TCP进行连接的3次握手流程,先初始化一个ISN的随机值,发送到目的地一个连接请求。而后续TCPF报文段中的序号值将被系统设置成ISN加上该报文的端所携带书籍的第一个字节在整个字节流中的偏移值。例如ISN+1025.而32位确认号跟32位序号类似,由目的端发送一个确认的随机ISN或增加一定的偏移值反还到请求端。后续请求端将会带着目的端的这个确认号进行数据交互。4位头部长度是TCP头部最长是60字节。6位标志标识 URG紧急指针是否有效,ACK确认号是否有效,PSH提示接收端需要立即从TCP接收缓存区中读走数据,RST要求对方重新建立连接,SYN请求建立一个连接,FIN通知对方端要关闭连接了。16位窗口大小是TCP流量控制的手段,告诉对方本端TCP接收缓存区剩余容纳的字节数。这样对方就可以控制发送数据的速度。16位校验和是由发送端填充,可以让接收端通过CRC算法进行(报文头和数据)验证TCP报文段在传输的过程中是否损坏。16位紧急指针是一个正的偏移值,它和序号值相加表示最后一个紧急数据的下一个字节序号。
3、TCP建立连接和断开
由上图可了解一个TCP连接主要进行了3次握手流程和4次挥手流程。
握手流程:1)主机A向主机B发送了一个TCP报文段,其中包含了请求建立连接的SYN标识,并且携带了一个x序号值。2)主机B接收到了主机A的报文,并且对上一个报文进行了ACK确认号和同意连接的SYN标识,并且回复一个序号y值和一个x+1的确认序号。3)主机A接收到主机B的确认后又向主机B发送了一个ACK确认号和y+1的确认序号,双方就这样建立了连接。
关闭流程:1)主机A向主机B发送一个序列号为x值,并且发送了一个FIN的请求关闭连接的标识,又返回了一个上次建立连接时的确认号。2)主机B接收到请求后返回一个ACK确认号,并返回一个x+1的确认序号将同意关闭连接。3)然后主机B又紧接着向主机B发送一个关闭连接的FIN信号,并且返回了上次确认关闭连接的确认号x+1的序列号。又返回了一个主机A向主机B请求关闭的序列号x值。4)主机A接收到B的关闭结果后,又向主机B回复了一个请求关闭连接序列号x+1的值和ACK确认号,说明关闭结束了。
4、TCP的状态转移过程
TCP状态转移是指TCP从连接到关闭的整个过程是3次握手和4次挥手的转移细节
由上图可以了解,绿色代表服务器端连接状态转移,红色代表客户端连接状态转移,而蓝色代表关闭总流线。而 closed 是一个假象的启始点。
绿色路线:服务端接监听(listen)到客户端请求连接,首先进入了listen状态,被动等待客户端的请求,直到监听到某一个请求过来,就将该连接放入到内核等待队列中,并向客户端发送一个SYN标志的确认报文段。此时连接状态处于SYN_RCVD。如果服务器成功接收到客户端的确认报文段,则进入双向数据传输的ESTABLISHED状态。当客户端主动关闭连接时,服务器通过返回确认报文进入CLOSE_WAIT状态等待服务器应用程序关闭连接。通常检测到客户端关闭连接后,也会立即给客户端发送一个结束的报文段来关闭连接。这使得连接进入LAST_ACK状态。等待客户端对结束报文的最后一次确认,一旦确认完就彻底关闭连接。
红色路线:客户端向服务器端发送连接过程,客户端主动向服务器端发送connect请求,使得状态转移到SYN_SENT。在这个状态下有以下两个失败而退出close的因素。1)如果请求的目标端端口不存在或者端口处于繁忙状态(TIME_WAIT被占用了)则服务器讲给客户端发送一个复位报文段,connect调用失败。2)如果端口存在,但是connect超时未收到服务器端的确认报文,则connect失败。那如果一旦连接成功,则连接状态转移至ESTABLISHED,这个时候就可以进行数据传递和读写了。如果客户端执行主动关闭,将向服务器端发送一个结束报文,同时连接状态进入FIN_WAIT_1.此时客户端收到服务器端的确认报文后,则连接状态转移到FIN_WAIT_2.这个时候服务器端的状态处于CLOSE_WAIT状态。这个时候两端的状态都是处于半关闭等待状态。如果客户端如果等不到服务器端发送的确认结束报文段,客户端状态将停留在FIN_WAITE_2.如果服务器端将给予确认关闭连接(结束报文段)则客户端才能进入TIME_WAIT状态。特别注意:如果这个时候客户端没有收到服务器端的确认关闭连接,则客户端的连接由内核来管理,这个时候称为孤儿连接。孤儿连接由孤儿连接数和孤儿生存时间控制寿命。
TIME_WAIT的存在价值1)可靠的终止TCP连接。2)保证让迟来的TCP报文段有足够的时间识别并丢弃。
5、TCP交互数据流和成块数据流
前面说了TCP连接的状态,这个一段讨论TCP连接交换的应用程序数据。TCP报文段所鞋底啊的应用程序数据是按照长度分为两种:交互数据流 和 成块数据流;
交互数据流:实时性要求高,比如:telnet、ssh等
成块数据流:长度通常是TCP报文段的最大数据长度,对传输效率要求高,比如:ftp
交互数据流是说我们在通过telnet连接了一个远程的服务器时,进行输入密码,回车换行以及ls等每一个动作都会产生一个报文回复。也就是客户端每发送一个请求,服务器端都会发送一个确认的报文段都包含在要发送的应用程序内。也就是说服务器端确认回复和需要返回显示数据的一起返回客户端。服务器端的这种处理方式为延迟确认。也就是它不是马上确认上次收到的数据,而是在一段延迟时间后看有没有徐亚发送的数据要显示,如果有,这和确认信息一起发出去。因为服务器对客户端请求的速度特别快,从而延迟确认减少了发送TCP报文段的数量。但是这种状态在局域网中没有问题,对于广域网来说还存在很大的缺陷。这个时候就出现了Nagle的算法,解决了这样的问题。Nagle算法要求换一个TCP连接的通信双方在任意时间最多只能发送一个未被确认的TCP报文段。在未得到确认之前不能发送其他TCP报文段,另外,发送方在等待确认时收集本段要发送的微量数据,待确认到来时一并发送,该算法还有个优点是自适应性,确认到达得越快,数据发送也就越快。TCP在发送成块数据流的时候,也不会每收到一个报文段就发送一个确认,而是通常使用“隔一个报文段确认”策略。
超时重连
TCP如何控制数据传输已保证可靠性:TCP服务必须能够重传超时时间内未收到确认的TCP报文段。所以TCP模块为每一个TCP报文段维护一个重传定时器。默认一共执行多次重传,每次重传的超时时间增加一倍,分别是0.2s、0.4s、0.8s、1.6s、3.2s等。在5次都失败的情况下由底层的IP和ARP接管。直到客户端放弃连接为止。
拥塞控制
拥塞控制的四个方法:慢启动,拥塞避免,快速重传,快速恢复。拥塞控制算法在linux下由多种实现:比如 reno算法、vegas算法、cubic算法