1 UDP
-
定义:用户数据报协议(User Datagram Protocol),位于传输层,提供了一种无需建立连接就可以发送封装的 IP 数据报的方法
-
特性:
- 速度快(无需建立连接)
- 适合于发送数据量小的应用(聊天软件、DHCP、直播等)
- 分组首部开销小
-
封装格式:
-
源端口(16位):通常包含发送数据报的应用程序所使用的 UDP 端口,接收端的应用程序利用这个字段的值作为发送响应的目的地址。这个字段是可选的,如果不写入端口号,则把这个字段设置为 0,这样,接收端的应用程序就不能发送响应了
-
目的端口(16位):接收端计算机上 UDP 软件使用的端口
-
长度(16位):表示 UDP 数据报长度,包含 UDP 报文头和 UDP 数据长度。因为 UDP 报文头长度是 8 个字节,所以这个值最小为 8
-
校验和(16位):检测UDP数据在传输中是否出错,有错则丢弃
-
-
相关服务(端口号):
- DNS:53
- TFTP:69
- SNMP:161
2 TCP
-
传输控制协议(Transmission Control Protocol),位于传输层,提供面向连接和可靠的字节流传输服务
-
特性:
- 面向连接(速度慢)
- 可靠传输,适合用于发对完整性要求高和较大的文件
- 拥有流量拥塞控制
-
封装结构:
-
源端口和目的端口(16位)
-
***(32位):Sequence Number,***用来标识从TCP发送端向接入端发送的数据字节流进行编号,可以理解成对字节流的计数。首部中的***字段值指的是本报文段所发送的数据的第一个字节的序号
-
确认号(32位):Acknowledgment Number,若确认号=N,则表明:到序号N-1为止的所有数据都已正确收到,期望收到对方下一个报文段的第一个数据字节的序号(期望对方开始发送序号N之后的数据)。确认号只有在ACK标志为1时才有效
-
头部长度(4位):也叫数据偏移(我觉得这个名字不太好理解),指TCP报文段的首部长度,以4字节为单位,最小为基本头部20字节(即不含选项),则该字段数值为20/4=5;最大为60字节,则该字段数值为60/4=15
-
保留位(6位):保留为今后使用,目前置0
-
控制位(6位):
- URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针
- ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段
- PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队
- RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求
- SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1
- FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流
-
窗口大小(16位):滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制
-
校验和(16位)
-
紧急指针(16位):紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此,紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作
-
选项和填充(0~40字节)
-
相关服务(端口号):
- ftp:20、21
- ssh:22
- telnet:23
- smtp:25
- dns:53
- http:80
3 TCP协议状态机
| 状态 | 说明 |
|---|---|
| CLOSED | 这个状态不是一个真正的状态,是图中假想的一个起点或者是终点 |
| LISTEN | 服务器等待连接过来的状态 |
| SYN_SENT | 客户端发起连接(主动打开),变成此状态,如果SYN超时,或者服务器不存在直接CLOSED |
| SYN_RCVD | 服务器收到SYN包的时候,就变成此状态 |
| ESTABLISHED | 完成三次握手,进入连接建立状态,说明此时可以进行数据传输了 |
| FIN_WAIT_1 | 客户端执行主动关闭,发送完FIN包之后便进入FIN_WAIT_1状态 |
| FIN_WAIT_2 | 客户端发送FIN包之后,收到ACK,即进入此状态,其实就是半关闭的状态 |
| CLOSE_WAIT | 接收到FIN 之后,被动关闭的一方进入此状态。具体动作是接收到 FIN,同时发送 ACK。之所以叫 CLOSE_WAIT 可以理解为被动关闭的一方此时正在等待上层应用程序发出关闭连接指令 |
| CLOSING | 两边同时发出FIN请求 |
| LAST_ACK | 被动的一端发送FIN包之后 处于LAST_ACK状态 |
| TIME_WAIT | 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态 |
- 客户端的状态变迁:CLOSED–>SYN_SENT–>ESTABLISHED–>FIN_WAIT_1–>FIN_WAIT_2–>TIME_WAIT–>CLOSED
- 服务器的状态变迁:CLOSED–>LISTEN–>SYN_RCVD–>ESTABLISHED–>CLOSE_WAIT–>LAST_ACK—>CLOSED
4 三次握手
-
三次握手过程分析:
- 首先A向B发出连接请求报文段,这时首部中的同步位SYN=1,同时选择一个初始序号 seq=x。TCP规定,SYN报文段不能携带数据,但要消耗掉一个序号。这时,A进入SYN_SENT状态
- B收到请求后,向A发送确认。在确认报文段中把SYN和ACK位都置为1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时B进入SYN_RCVD状态
- A收到B的确认后,还要向B给出确认。确认报文段的ACK置为1,确认号ack=y+1,而自己的序号seq=x+1。这时,TCP连接已经建立,A进入ESTABLISHED 状态,当B收到A的确认后,也会进入 ESTABLISHED 状态
-
为什么需要三次握手过程?
这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。
所谓已失效的连接请求报文段是这样产生的。A发送连接请求,但因连接请求报文丢失而未收到确认,于是A重发一次连接请求,成功后建立了连接。数据传输完毕后就释放了连接。现在假定A发出的第一个请求报文段并未丢失,而是在某个网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误以为A又发了一次新的连接请求,于是向A发出确认报文段,同意建立连接。假如不采用三次握手,那么只要B发出确认,新的连接就建立了。
由于A并未发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据,因此白白浪费了许多资源。
采用TCP三次握手的方法可以防止上述现象发生。例如在刚才的情况下,由于A不会向B的确认发出确认,连接就不会建立。 -
如果在TCP第三次握手中的报文段丢失了会发生什么情况?
Client认为这个连接已经建立,如果Client端向Server写数据,Server端将以RST包响应,方能感知到Server的错误。
5 四次挥手
-
四次挥手过程分析
- 客户端 A 的 TCP 进程先向服务端发出连接释放报文段,并停止发送数据,主动关闭 TCP 连接。释放连接报文段中 FIN=1,序号为 seq=u,该序号等于前面已经传送过去的数据的最后一个字节的序号加1。这时,A进入 FIN_WAIT_1(终止等待1)状态,等待 B 的确认。TCP 规定,FIN报文段即使不携带数据,也要消耗掉一个序号。这是 TCP 连接释放的第一次挥手。
- B收到连接释放报文段后即发出确认释放连接的报文段,该报文段中,ACK=1,确认号为ack=u+1,其自己的序号为v,该序号等于B前面已经传送过的数据的最后一个字节的序号加1。然后B进入CLOSE_WAIT(关闭等待)状态,此时TCP服务器进程应该通知上层的应用进程,因而A到B这个方向的连接就释放了,这时TCP处于半关闭状态,即A已经没有数据要发了,但B若发送数据,A仍要接受,也就是说从B到A这个方向的连接并没有关闭,这个状态可能会持续一些时间。这是TCP连接释放的第二次挥手。
- A收到B的确认后,就进入了FIN_WAIT_2(终止等待2)状态,等待B发出连接释放报文段,如果B已经没有要向A发送的数据了,其应用进程就通知TCP释放连接。这时B发出的链接释放报文段中,FIN=1,确认号还必须重复上次已发送过的确认号,即ack=u+1,序号seq=w,因为在半关闭状态B可能又发送了一些数据,因此该序号为半关闭状态发送的数据的最后一个字节的序号加1。这时B进入LAST_ACK(最后确认)状态,等待A的确认,这是TCP连接的第三次挥手。
- A收到B的连接释放请求后,必须对此发出确认。确认报文段中,ACK=1,确认号ack=w+1,而自己的序号seq=u+1,而后进入TIME_WAIT状态。这时候,TCP连接还没有释放掉,必须经过时间等待计时器设置的时间2MSL后,A才进入CLOSED状态,时间MSL叫做最长报文寿命,RFC建议设为2分钟,因此从A进入TIME_WAIT状态后,要经过4分钟才能进入到CLOSED状态,而B只要收到了A的确认后,就进入了CLOSED状态。二者都进入CLOSED状态后,连接就完全释放了,这是TCP连接的第四次挥手。
-
形象的描述
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,“告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,“告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,“就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
-
为什么需要四次挥手?
- 为了保证A发送的最后一个ACK报文段能够到达B。即最后这个确认报文段很有可能丢失,那么B会超时重传,然后A再一次确认,同时启动2MSL计时器,如此下去。如果没有等待时间,发送完确认报文段就立即释放连接的话,B就无法重传了(连接已被释放,任何数据都不能出传了),因而也就收不到确认,就无法按照步骤进入CLOSE状态,即必须收到确认才能close。
- 防止“已失效的连接请求报文段”出现在连接中。经过2MSL,那些在这个连接持续的时间内,产生的所有报文段就可以都从网络中消失。即在这个连接释放的过程中会有一些无效的报文段滞留在楼阁结点,但是呢,经过2MSL这些无效报文段就肯定可以发送到目的地,不会滞留在网络中。这样的话,在下一个连接中就不会出现上一个连接遗留下来的请求报文段了。
7 通过wireshark抓包理解TCP
8 UDP&TCP的区别
-
TCP基于连接;UDP基于无连接
-
TCP要求系统资源较多,首部开销20字节;UDP较少,首部开销8字节
-
UDP程序结构较简单
-
TCP面向字节流传输;UDP面向数据报传输
-
TCP能保证数据顺序;UDP不能保证
-
TCP保证数据可靠;UDP可能丢包
-
TCP有拥塞控制;UDP没有
-
TCP的确认机制、三次握手机制容易被利用实现DOS、DDOS等攻击;UDP比TCP稍安全
-
TCP编程的服务器端一般步骤是:
- 创建一个socket,用函数socket();
- 设置socket属性,用函数setsockopt();
- 绑定IP地址、端口等信息到socket上,用函数bind();
- 开启监听,用函数listen();
- 接收客户端上来的连接,用函数accept();
- 收发数据,用函数send()和recv(),或者read()和write();
- 关闭网络连接;
- 关闭监听;
-
TCP编程的客户端一般步骤是:
- 创建一个socket,用函数socket();
- 设置socket属性,用函数setsockopt();
- 绑定IP地址、端口等信息到socket上,用函数bind();
- 设置要连接的对方的IP地址和端口等属性;
- 连接服务器,用函数connect();
- 收发数据,用函数send()和recv(),或者read()和write();
- 关闭网络连接;
-
UDP编程的服务器端一般步骤是:
- 创建一个socket,用函数socket();
- 设置socket属性,用函数setsockopt();
- 绑定IP地址、端口等信息到socket上,用函数bind();
- 循环接收数据,用函数recvfrom();
- 关闭网络连接;
-
UDP编程的客户端一般步骤是:
- 创建一个socket,用函数socket();
- 设置socket属性,用函数setsockopt();
- 绑定IP地址、端口等信息到socket上,用函数bind();
- 设置对方的IP地址和端口等属性;
- 发送数据,用函数sendto();
- 关闭网络连接;