IP协议
IP协议是 无连接
的通信协议, 作用在网络层 *主要作用是:负责寻址和路由选择,即将网络地址翻译为物理地址然后确定如何将数据从发送方路由到接收方.
它不会占用两台正在通信的计算机之间的通信线路, 这样IP协议就降低了
对网络线路的需求,每条线可以同时满足许多不同计算机之间的通信需要.
通过IP协议,消息或者其他要传送的数据会被分割为较小的独立的包然后在网络中传送,这种被分割成的包叫做数据包.
IP协议 只负责将每个包路由至它的目的地,但是IP协议没有做任何事情来确认数据包是否按顺序发送或者包是否被破坏,这个需要它的上层传输层中的协议来解决.
传输层控制协议TCP
- 面向连接的,可靠的,全双工的(两边都可以发送也可以接收),基于字节流的传输层通信协议.
- 将应用层的数据流分割成
报文段
并发送网络层. 注意:应用层以上数据单位都是报文,数据链路层是数据帧,网络层是数据包,物理层是比特流 - TCP协议为了保证不丢失包,每个数据包都有序号(seq Sequence Number),对方收到则发送
ACK
确认,如果发送端在合理的往返时延TCP_TIMEOUT_INIT内未收到确认
则重传. - TCP协议用一个奇偶校验和函数来检验数据在传输过程中是否有误.
TCP报文头
如图:
TCP Flags
- URG: 紧急指针标志,为1表示紧急指针有效,为0则忽略紧急指针.
- ACK:确认序号标志,为1表示这是一个用于确认的报文.
- PSH: push标志.
- RST: 重置连接标志.
- SYN:同步序号标志,用于建立连接过程,为1表示这是一个请求建立连接的报文.
- FIN:结束标志,用于释放连接,为1时表示发送方已经没有数据发送了,关闭连接.
TCP的三次握手
握手是为了建立连接,TCP三次握手流程图如下:
- 刚开始客户端和服务器端都会处于关闭状态,当双方打开之后,服务端便立刻进入
LISTEN即监听状态
. - 客户端首次向服务器端发送请求报文,此时同步序号标志SYN为1,而序号seq为任意正整数值x,向服务器端发送完报文后,客户端便进入了名为
SYN_SENT(同步_已发送)
的状态.在此时之前发送的那个报文段被称为SYN报文段
,这个报文不能携带数据
,但是会消耗一个序号,
这就是第一次握手!!! - 当服务器接收到请求报文以后,先确认客户端发来的请求报文, 如果同意连接,则向客户端发出
确认报文,
确认报文中的SYN同步标志位为1
,ACK确认标志位为1
,序列号为y(任意正整数)
,确认号ack为x+1(客户端传过来的序列号+1)
.给客户端发送确认报文之后,服务器便进入了SYN_RCVD(同步_已接收)
阶段,这个报文也不能携带数据,但是仍然会消耗一个序列号,这就是 第二次握手. - 当客户端接收到确认报文之后,先检查服务器端发来的确认报文,然后向服务器发出一个确认报文,其中
ACK确认序号标志为1
,seq序号为x+1
,ack确认号为y+1(服务器发给我的序号是y,我作为回应要还给你,但是又消耗了一个序号,所以+1)
,这个报文 是可以携带数据,会消耗一个序列号,不携带数据就不消耗序号 这就是 第三次握手. - 客户端将确认报文发送给服务器端之后,客户端就进入了
ESTABLISHED已建立连接
的状态. - 服务器端收到确认报文后,服务器端便也随之进去了
ESTABLISHED已建立连接
的状态,此时可以发送数据了.
总结
小知识:
- TCP和UDP数据包中都是不包含ip信息的,因为ip是网络层的事情,而他俩在传输层.
- TCP和UDP数据包中都会有
源端口
和目的端口
,也就是说,端口是在传输层的. - 两个进程在同一台计算机内部进行通信,可以被
PID(进程号)
来唯一标识某个进程,但是这个只在本地唯一. - 如果把两个进程放在了不同的计算机,那么如果要进行通信的话,PID是不能唯一定位到两个进程的,解决方法就是使用在传输层中的端口号来唯一标识某个进程
- IP地址+端口号+协议号(标识上层是TCP还是UDP)可以唯一标识某一台主机的某个进程.
为什么需要三次握手才能建立起连接?
-
为了初始化
Sequence Number
的初始值,让双方知道对应的seq,也就是上面的x和y,这个序列号是以后进行通信的序号,来保证应用层接收到的数据不会因为网络等问题而乱序,之后TCP会用这个序号来拼接数据
,所以会有第三次握手,客户端通知服务器端已经收到服务器端的seq的初始化值. -
如果只有两次握手,那么服务器端收到了客户端传来的
SYN=1的请求连接数据包之后
,便会分配资源并向客户端发送ACK=1的确认连接数据包
这样会产生一个问题: 当客户端与服务器端正常建立连接,发送数据结束关闭TCP连接之后,如果在之前客户端与服务器端建立连接过程中残留的数据包此时才到达服务器端,那么
服务器端就会认为客户端又申请了一次连接,然后又给客户端分配资源发送确认连接报文
,但是此时是没有数据会发送给服务器端的,服务器就只能一直等着,知道超时,这就造成了服务器端资源的浪费.
首次握手会有一个隐患:—>SYN超时问题
SYN超时问题是指: 如果服务器端
收到了客户端
的SYN
,向客户端回复SYN-ACK
报文之后客户端发生了掉线了之类的情况,导致服务器端未收到客户端发送的ACK确认的情况.
当服务器端没有收到客户端的ACK确认报文前,此时暂时双方就处于一个中间状态,没有成功也没有失败,一定时间后
如果服务器端还没有收到客户端发来的ACK报文后
,服务器端
就会不断重试(即重发SYN-ACK
报文至客户端)直至超时,linux下默认重试次数为5次,重试的间隔时间从1秒开始,每次都翻倍,比如:1s->2s->4s->8s->16s,第五次发出去之后,再等待32s,还没收到就判定位超时,断开连接(即63s后TCP断开连接).
这样子可能会使得服务器遭受SYN Flood
攻击,即恶意程序给服务器发一个SYN
报文之后就不发ACK
报文了,服务器每次都会等待一段时间才断开连接,次数太多了,会使得SYN半连接队列(server收到SYN请求连接报文后将client放到半连接队列)耗尽
,会导致其他正常的请求无法处理.
SYN Flood攻击解决办法(有好几个,我只写一个嘿嘿嘿):
其一就是: 当server收到client的SYN之后,不立即分配资源,而是根据client发送过来的SYN计算出一个cookie值
,这个cookie值用来 存储server返回给client的SYN+ACK数据包中的初始序列号seq,当client发送第三次握手的ACK包之后进行校验,如果校验成功则server分配资源,建立连接.那么当syn半连接队列满了之后没有进行校验的请求就会被抛弃
.
如果已经建立连接,客户端出现故障怎么办?
TCP有保活机制: 也就是说,TCP设有保活计时器
,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间一般是俩小时,如果俩小时服务器端还没收到客户端的任何数据,即连接处于非活动状态,则服务器端会向客户端发送探测报文
,如果未收到响应,会每隔75秒钟发送一个,若连续发送10个仍然没反应,服务器端就会断开连接.