TCP头格式:
源端口号(16位),目的端口号(16位)
序列号(32位)
确认应答号(32位)
首部长度(4位),保留字段(6位),控制位(ACK,RST,SYN,FIN)(6位), 窗口大小(16位)
校验和(16位),紧急指针(位)
源IP和目的IP都在IP包里面,TCP是运输层的,只负责端口到端口的传输。
为什么需要 TCP 协议? TCP 工作在哪一层?
TCP协议是可以提供可靠的数据传输服务。主要是工作在传输层。
TCP的三个特性
面向连接:只能一对一的连接
可靠:即使出现网络波动,都会有重传机制等保证报文完整到达。
字节流:在TCP中,数据被看做一个连续的字节流,而不是离散的消息或包。TCP会在传输层将字节分割成合适的段。每个段都是有序号用于将字节流重新组装回原始顺序。
UDP和TCP的区别?
UDP头只有8个字节。
源端口号(16位),目标端口号(16位)
包长度(16位),校验和(16位)
- 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
- 包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
- 校验和:校验和是为了提供可靠的 UDP 首部和数据而设计,防止收到在网络传输中受损的 UDP 包。
TCP 和 UDP 可以使用同一个端口吗?
答案是可以的.
内核中的TCP模块和UDP模块是两个完全独立的软件模块。
电脑会根据IP包里面得到的协议类型送到相应的模块,模块会将其送到对应的应用程序。
TCP连接的三次握手过程是怎么样的?
1.首先服务端要处于监听状态
2.客户端发送SYN包表示想创建连接。然后客户端进入SYN_SENT状态。
3.服务端接收到SYN包后响应一个SYN+ACK包,然后服务端进入SYN_RCVD
4.客户端收到SYN+ACK包后,响应一个包,然后进入到连接完成状态。
5.服务端收到ACK之后也进入到连接完成状态。
第三次握手是可以携带数据的,因为客户端已经进入了连接完成的状态。
为什么TCP握手是三次?
三次是保证双方的发送接收能力的最少次数。
正解:
三次握手可以阻止重复历史连接的初始化(主要原因),避免资源的浪费.
客户端第一次发送的请求(seq=90)因为网络原因迟迟没到,然后客户端新发送了一个请求(seq=100)。这时旧请求先到了服务端,然后响应了个91,客户端发现这个不是自己要的,就会发送一个RST报文中止连接。一段时间后新请求才到达服务端。
结论:两次握手的话,服务端在收到就报文时就会创建一个历史连接了,造成资源浪费。
三次握手可以同步双方的初始化序列号
结论:三次握手能够保证双方的序列号都被对方正确接收。两次握手只能保证一方的序列号被正确接收。
为什么每次建立TCP连接时,初始化的序列号都要求不一样呢?
1.防止历史报文被下一个TCP连接接收.
如果每次序列都从0开始的话,可能上一次连接的一个序列号100的报文经过网络波动会处于下一个连接的接收窗口里面。
2.防止黑客伪造的相同序列号的TCP报文被对方接收。
初始序列号 ISN 是如何随机产生的?
起始ISN是基于时钟,每4微秒+1,转一圈要4.55小时。
ISN=M+F(源IP,目的IP,源端口,目的端口)。
M是基于时间递增的,F是用的HASH算法计算得到。基本不可能会随机成一样的初始化序列号。
为什么TCP报文是在传输层进行分片呢?明明IP层也可以
IP层在接收到一个大于MTU的数据包时就会进行分片。
这里存在一个隐患,一个IP分片丢失了,整个IP报文的所有分片都要重传。
IP层没有超时重传,这主要靠传输层完成。
TCP长时间接收不到ACK就会直接重传整条消息,也就是重传了所有IP分片。
什么是SYN攻击?
讲SYN攻击之前首先要知道两个队列。
TCP半连接队列,TCP全连接队列。
这两个队列工作流程如下
正常流程:
- 当服务端接收到客户端的 SYN 报文时,会创建一个半连接的对象,然后将其加入到内核的「 SYN 队列」;
- 接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;
- 服务端接收到 ACK 报文后,从「 SYN 队列」取出一个半连接对象,然后创建一个新的连接对象放入到「 Accept 队列」;
- 应用通过调用
accpet()
socket 接口,从「 Accept 队列」取出连接对象。
两个队列都是有长度限制的,超出之后,默认都会丢弃报文。
SYN攻击就是把半连接队列打满,后续再收到SYN报文时就会直接丢弃了。
避免方式:
1.增大半连接队列。
2.减少SYN+ACK重传次数 (这样就能让连接存活时间变短)
3.开启tcp_syncookies;(可以在不使用半连接队列下成功建立连接,如下)
4.调大 netdev_max_backlog(当内核处理速度跟不上网卡接收速度时会有一个队列接收这些数据包)
TCP的四次挥手过程
只有主动关闭的一方才会有时长为2MSL的TIME_WAIT状态
MSL是数据包在网络中的最长存活时间。
这是在允许报文丢失一次。假如ACK包在第一个MSL丢失了,对方会重发FIN包。
重新接收到FIN包之后,2MSL又会重新开始了。
socket编程
客户端和服务端都会初始化socket,得到文件描述符。
实际上服务端用来监听的socket和用来传输的socket不是同一个。
如何理解字节流?
操作系统对 TCP 和 UDP 协议的发送方的机制不同,所以说TCP是面向字节流的协议,UDP是面向报文的协议。
为什么UDP 是面向报文的协议?
用户消息用UDP传输时是不会在传输层对它进行拆分的。
每个报文就是一个用户消息的边界。
操作系统会有一个UDP报文队列,每一个元素就是一个UDP报文。
为什么TCP是面向字节流的协议?
用户消息使用TCP协议传输时,消息会在传输层被分成多个TCP报文。接收方不知道消息长度就无法读出一个有效的用户消息。
系统要发送“北岭山脚”和“鼠鼠”两个用户消息。
虽然系统是调用了两次send(),但是两个用户消息可能是分到同一个TCP报文。
变成一个TCP报文里面有“北岭山脚鼠鼠”,这个就是所谓粘包问题。
所以不能认为一个TCP报文对应一个用户消息,因此,TCP是面向报文的协议。
粘包问题解决
- 固定长度的消息;
- 特殊字符作为边界;
- 自定义消息结构:自定义一个消息结构由包头和数据组成。包头里面有一个字段专门用于说明数据大小。
TCP包中的序列号和确认号如何变化?
ack包的数据长度为0,所以上一个包有ack标识的话是直接+0;