TCP协议学习笔记

TCP是面向连接的,可靠的,基于字节流的传输层通信协议。

面向连接:一对一的连接,不像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多是无法实现的

可靠的:无论网络链路中出现怎么样的链路变化,TCP都能保证一个报文一定能够到达接收端。

字节流:消息是没有边界的,无论消息有多大都可以进行传输。

TCP能够被广泛使用的原因:

1.数据可靠,必达 2.传输效率不低 3.数据有顺序


TCP 头部格式

TCP包头格式

struct tcphdr{

unsigned short sport; // 源端口号  2个字节 16bit

unsigned short dport; // 目的端口号 2个字节 16bit

unsigned int seqnum;     // 序列号   4个字节  32bit 每一个tcp包都会携带一个序列号,开始是随机产生的一个初始值然后开始增加,超过最大值后从头开始。用来解决网络包乱序的问题。

unsigned int acknum; // 确认号   4个字节  32bit 三次握手第二次返回的时候  带acknum的值;指下一次期望收到的数据的序列号,用来解决不丢包的问题。

unsigned char hdrlen:4,  // 头部长度  4bit 最大值是1111 即 15 , 单位是4字节, 所以最大值是15*4 = 60字节;tcp头部最大值是60字节。

resv:4;

unsigned char cwr:1,

ece:1, //cwr  与 ece  理解为填充左右

urg:1, // 1紧急指针位 1bit 如果标志1,数据发送之后,对端收到后会立即从缓冲区发送给应用程序处理

ack:1, // 2确认位 该字段为1,确认应答的字段为有效,TCP规定除最初建立连接的SYN包之外的该位必须置为1。

psh:1, //

rst:1, // 该位为1时,表示TCP连接中出现异常必须强制断开连接

syn:1, //该位为1时,表示希望建立连接,并在其序列号字段进行序列号初始值的设定

fin:1; //该位为1时,表示今后不会再有数据发送,希望断开连接。

unsigned short cwnd; // 窗口大小,2个字节 16bit

unsigned short check; // 校验和 2个字节 16bit

unsigned short usr_point; // 紧急指针 2个字节 16bit

};

如何确定一个TCP连接?

TCP四元组可以唯一确定一个连接:源地址,源端口,目的地址,目的端口。

源地址和目的地址的字段(32位)在IP头部中;源端口和目的端口的字段(16位)在TCP头部中。

一个IP的服务器监听一个端口,TCP最大连接数是多少?

根据四元组,固定了目的ip地址和目的端口,可变的是源ip地址和源端口号,最大值是=源ip地址X源端口号。

对于ipv4,客户端IP数最多为2的32次方,端口号最多为2的16次方,最大为2的48次方

以上是理论值,实际受到系统文件描述符数量的限制,可以通过ulimit查看修改,另外受到内存大小的限制,每一个连接都是需要占用内存的。

UDP和TCP有何区别?分别适用于什么场景?

连接:TCP传输数据之前需要先建立连接;UDP不需要建立连接。

服务对象:TCP是一对一的连接;UDP支持一对一,一对多,多对多的交互通信。

可靠性:TCP可靠交付数据的,UDP不保证可靠交付数据。

首部开销:TCP首部较长,20-60字节;UDP只有8个字节,开销小

TCP经常用于FTP文件传输,HTTP/HTTPS;UDP使用于视频音频等多媒体通信.

TCP数据长度计算:

tcp数据长度 = IP总长度 - IP首部长度 - TCP首部长度。


TCP三次握手:

第一次:客户端随机生成一个seq序列号,将此序列号置于TCP首部的序号字段中,同时syn标志位设置为1,表示SYN报文。接着向服务器发送一个请求连接的报文,该报文不包含应用层数据,之后客户端处于SYN-SEND状态。

第二次:服务器收到SYN报文后,随机生成一个seq序列号,将此序列号放置在TCP首部的序列号字段中,同时SYN标志位设置为1,acknum设为为收到的seq+1,表示对收到数据的确认,也可以理解为下次期望收到的数据的起始编号,将ACK标志位设置为1,。最后将该报文发送给客户端,该报文不包含应用层数据,之后服务器处于SYN-RCVD状态。

第三次: 客户端收到服务端报文后,还需要向服务器发送一次确认,确认应答号字段被填入 收到的序列号+1,同时ACK标志位设置为1,最后把报文发送个服务端,这次报文可以携带客户端到服务器的数据,之后客户端处于ESTABLISHED状态。服务器收到客户端的应答报文后,也进入ESTABLISHED状态。

可以发现三次握手中,前两次是不可以携带数据的,第三次是可以携带数据的。

如何在linux中查看TCP状态?

netstat -napt

TCP握手为什么是三次,不是两次或者四次?

    首先理解 TCP连接是什么,tcp连接是用于 保证可靠性 和 流量控制维护 的某些状态信息,包括socket,序列号,和窗口大小称之为连接。

两次连接 无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠同步双方的序列号;

四次握手,三次握手可以建立可靠性连接,就不在需要更多的通信次数了。

为何初始序号客户端和服务端ISN是不同的?

网络中报文会延迟,会复制重发,也有可能丢失,这样会造成不同连接之间产生互相影响,为了避免互相影响,客户端和服务端初始序号是随机且不同。

既然IP会分片,那么为什么TCP还需要MSS呢?

MTU:一个网络包的最大长度,以太网中一般1500字节;MTU = IP头部 + TCP头部 +TCP数据

MSS:除去IP包头和TCP头部之外,一个网络包所能容纳的TCP数据的最大长度。MSS = TCP数据。

当IP层有一个超过MTU大小的数据,IP层就要进行分片,数据分成若干个片,保证每一片都小于MTU,之后由目标主机IP层进行重新组装后交给TCP传输层。

如果其中某一片丢失,IP层没有超时重传,TCP负责超时和重传,接收方发现TCP某一片丢失后,不会响应ACK给对端,发送方在TCP超时后就会重发整个TCP报文。由此可见TCP分片效率不高。

为了达到最佳的传输效能,TCP协议建立连接时通常要协商双方的MSS值,TCP层发现超过MSS时,会先进行分片,这样TCP层数据IP的长度就不会大于MTU了,自然不用IP分片了。如果由TCP分哦按丢失,重发也是以MSS为单位,不用重发所有的分片。

TCP四次挥手过程:

客户端打算关闭连接,发送一个TCP首部FIN标志位被设置为1 的报文,之后客户端进入FIN_WAIT1的状态。

服务器收到该报文后,向客户端发送ACK应答报文,接着服务器进入CLOSE_WAIT状态。

服务端如果有尚未发送的数据,进行发送,然后向客户单发送一个FIN标志位被设置为1 的报文,服务端进入LAST_AVK状态。

客户端收到FIN报文后,向服务器发送一个ACK报文,之后进入TIME_WAIT状态。

服务器收到ACK之后,进入CLOSE状态,至此 服务端已经完成连接的关闭。

客户端等待2MSL秒之后,自动进入CLOSE状态,至此客户端也完成连接的关闭。

time_wait时间为什么是2MSL?

2MSL是所有报文在网络中生成的最大时间,超过这个时间报文将被丢弃;TCP是基于IP协议的,IP协议中有个TTL字段,是IP数据包可以经过的最大路由数,每经过一个路由,此值就会减去1,当此值到0时,报文将被丢弃。

MSL与TTL的区别:MSL单位是时间,TTL是经过路由跳数,,所有MSL应该大于TTL消耗为0的时间。确保报文已被自然消亡。

等待2倍的MSL:网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待2倍的时间。

2MSL时间从客户端接收到FIN后发送ACK开始计时的,如果在TIME_WAIT时间内,因为ACK没有传输到服务器端,客户端又收到服务器端重发的ACK,那么2MSL将重新计时。

为什么存在TIME_WAIT状态?

防止有相同的四元组 (旧的数据包)被收到。

保证被动关闭的一方能够正确关闭:如果最后的ACK对端没收到,服务器一直处于last_ack状态,当客户端再次请求连接时,服务器会发送RST报文给客户端,连接的建立过程就会被终止。

TIME_WAIT过多的危害?

服务器有处于TIME_WAIT状态的TCP,说明服务器主动发起的断开请求,危害有两种:第一 内存资源占用,第二 端口资源的占用,一个TCP连接消耗一个本地端口。服务器TIME_WAIT状态过多,占满了端口资源,则会导致无法创建新的连接。

程序中使用SO_LINGER 

设置socket选项,设置调用close关闭连接行为


linger

l_onoff为1,linger为0,调用close后,立即发送一个RST标志位给对端,TCP直接跳过四次挥手进行关闭。危险行为,不值得提倡。

listen(fd,backlog)中backlog意义:

 backlog代表syn队列长度 或者 syn队列(mac下面)+accept队列长度(linux下)两个版本都有存在


accept函数完成的功能:

从队列中取出一个节点,分配一个fd与节点一一对应;accept队列为空时 accept阻塞。

connect成功返回在第二次握手,服务器端accept成功返回在三次握手成功之后

send/recv 需要考虑的问题:

1 如何保证顺序?  2 在顺序的前提下保证高效?

send的本质:把数据放入了内核中去,其他的什么也没有做;内核将数据发送到对端内核后,对端内核协议栈接收完毕后通知应用程序,应用程序调用recv。

如何做到高效:使用滑动窗口    


send、recv滑动窗口

滑动窗口两边变化:左边的值通过acknum变化的,右边的值通过ack+cwnd的值变化的,cwnd是对端回复ack时包头中携带的。

tcp如何保证顺序:延迟确认,收到一个包后 启动一个定时器,延迟200ms;延迟时间到之后,检查收到的包,在顺序接收完整序列的地方回复acknum,比如收到1-7  9-10序列的包,序列号为8的包没有收到,则回复acknum=8.表示8之前的包全部收到。

既然有TCP可靠性连接,为何还要用UDP做可靠性传输?

UDP应用场景应用为下载和实时性要求的场景,

UDP下载:UDP不带拥塞控制,TCP有窗口的计算,有个拥塞控制,限制了下载的过程中包,UDP没有这种限制。

UDP实时性:TCP有延迟确认,为了保证传输效率。UDP牺牲了传输效率,保证了实时性。

滑动窗口到底多大,如何确定窗口cwnd的大小?

根据网络上传输的时间,RTT 一个往返的时间,RTT增加,拥塞控制

RTT计算方式:RTT = 0.9 *old rtt + 0.1 *cur_rtt; 之前9次的rtt加上本次的rtt

cwnd开始默认值是1,之后变化按照 cwnd = 2*cwnd 进行变化,增长的过程中 有个门限值,到达门限值之后 线性增长;门限值之前的增长叫做慢启动,门限值之后的增长叫做拥塞控制。

拥塞控制 线性增长过程中 RTT时间内没有返回数据时候,cwnd窗口减少为之前的一半,重新进行拥塞控制。

你可能感兴趣的:(TCP协议学习笔记)