TCP与UDP
本篇是对网络上的知识进行汇总总结,仅做学习使用,侵删。
一、TCP/IP模型
上图是TCP/IP模型分层示意图,TCP/IP是指能够在多个不同网络间实现信息传输的协议簇。
- 应用层:为用户提供各种所需要的服务协议,如常用到的有HTTP(超文本传输协议)、FTP(文件传输协议)、Telnet(远程登录协议)等
- 传输层:为应用层实体提供端到端的通信功能,保证了数据的顺序传送和数据的完整性。该层定义了本次要重点归纳总结的两个协议:传输层控制协议(tcp) 和 用户数据报协议(udp)
- 网络层:主要解决主机到主机的通信问题(IP)
- 数据链路层与硬件层:不做过多的了解
二、TCP协议详解
TCP:传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
1.TCP报文结构
从网上搜罗了两张描述tcp报文结构的详解图,一起来了解下:
解析:
- 源端口与目的端口:各占2个字节,分别填入源端口和目的端口
- 序号(Sequence Port):占4个字节,用于标识每个报文段,使目的主机可确认已收到指定报文段中的数据。当源主机用于多个报文段传输同一个报文时,即使这些报文到达目的主机的顺序不一样,目的主机也能通过序列号按顺序排列它们。简而言之:用来对多个报文段进行排序
- 确认序号(Acknowledgment Number):占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号,若确认序号=N则到序号N-1为止的所有数据都已正确收到。
- 数据偏移:占1个字节,表示TCP报文段的首部长度。
- 保留:占6比特。
- URG(紧急):当URG=1,表明紧急指针字段有效。这时发送方TCP就把紧急数据插入到本报文段数据的最前面, 而在紧急后面的数据仍是普通数据。
- ACK(确认):当ACK=1,确认字段有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段 都必须把ACK置1。
- PSH(推送):接收方TCP收到PSH=1的报文段,就尽快地交付给接收应用进程,而不是等到整个缓存都填满了后再向上交付。
- RST(复位):当RST=1时,表示TCP连接中出现了严重差错,必须释放连接,然后重新建立运输连接。
- SYN(同步):在连接建立时用来同响应的报文段中使SYN=1和ACK=1.故SYN设置为1,就表示这是一个连接请求和连接接收报文。
- FIN(终止):用来释放连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
- 窗口:占2个字节,窗口值作为接收方让发送方设置发送窗口的依据
- 检验和:这里不做深究
- 紧急指针:占2字节,紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数
2.TCP三次握手
三次握手示意图:
整个流程为:
- 客户端主动打开,发送连接请求报文段,将SYN标识位 置为1,也就是SYN=1 ,seq=x(TCP规定SYN=1时不能携带数据,x为随机产生的一个值),然后客户端进程进入SYN_SEND状态。
- 服务器收到SYN报文段进行确认,将 SYN置为1,ACK置为1,即 SYN=1,ACK=1,seq=y,ack=x+1,然后服务端进程进入SYN_RECV状态,这个状态被称为半连接状态。
- 客户端再进行一次确认,将ACK置为1,seq置为x+1,ack置为y+1发送给服务器,最后客户端与服务器都进入ESTABLISHED状态。
那让我们想一想,前两步似乎已经足够完成连接,为什么还需要客户端再做一次确认呢?
拿打电话举例子,我给你打电话,相当于客户端第一次向服务端发起请求,而你接到我的电话并接通,相当于服务端收到请求并给予响应,那如果我在这两步完成后突然有事走开了,并且没知会你,你可能会一脸郁闷的挂断电话,但是服务器可没有你这么聪明,所以才有了第三次连接,便相当于接通电话后我向你说声“喂~”,正式开始通信。
稍微官方一点的说法则是:这主要是为了防止已经失效的连接请求突然又传回到服务器而重新建立连接占用资源。
补充:服务器端容易受到SYN攻击?
造成该现象的原因是因为客户端和服务端分配资源的步骤差异,服务端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的。正是这样的差异导致服务器容易受到SYN的洪泛攻击,即客户端在短时间内伪造大量不存在的IP地址,并向服务器端不断的发送SYN包,服务器端则回复确认包,并等待客户端的确认,由于源地址不存在,服务器端需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而导致
网络拥塞甚至系统瘫痪。
那如何去解决呢?
- 降低服务器端对SYN请求等待时间并尽快释放未连接的占用;
- 短时间内判断某IP的重复SYN则丢弃后续该SYN连接请求;
3.TCP四次挥手
tcp释放连接的过程:
- A的应用进程先向其TCP发出连接释放报文段(FIN=1,seq=u),并停止再发送数据,主动关闭TCP连接进入FIN-WAIT-1(终止等待1)状态,等待B的确认;
- B收到连接释放报文段后即发出确认报文段,(ACK=1,ack=u+1,seq=v),此时的TCP处于半关闭状态,A-B的连接释放;
- A收到B的确认报文后,进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放报文段。B没有要向A发出的数据,B发出连接释放报文段(FIN=1,ACK=1,seq=w,ack=u+1),B进入LAST-ACK(最后确认)状态,等待A的确认;
- A收到B的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),A进入TIME-WAIT时间等待状态,需要经过时间等待计时器设置的等待时间2MSL后,A才进入CLOSED状态
MSL(Maximum Segment Lifetime):报文最大生存时间,为什么需要在TIME-WAIT状态设置等待2MSL(LWIP为1分钟,
windows为2分钟):
1、保证A发送的最后一个ACK报文段能够到达B,从而进入close状态
这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到最后确认ACK报文段,B超时重传ACK+FIN,而A便能在2MSL等待时间内收到并重新向B发送ACK报文段重置计时器
若A在LAST-ACK状态不等待2MSL,若出现故障,最后的ACK报文段未能正常到达B,则B不能正常进入到close状态。
2、防止lost duplicate对后续新建正常连接的传输造成破坏,经2MSL,就可以使得本连接持续的时间内所产生的所有报文段都从网络中消失。
那为什么连接时是三次,断开时却需要四次呢?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
4. Java中对TCP协议的支持
java中对tcp协议的支持主要是两个类,一个是客户端类,Socket;一个是服务器类,ServerSocket,并通过io流进行数据传递
下面是一个非常简单的例子:
客户端:
//客户端
public class Client {
public static void main(String[] args) throws Exception{
//创建客户端对象,绑定连接的主机和端口
Socket socket = new Socket("localhost", 8888);
//获取服务端的响应信息
Scanner sca = new Scanner(socket.getInputStream());
while (sca.hasNextLine()) {
String line = sca.nextLine();
System.out.println(line);
}
sca.close();
socket.close();
}
}
服务器端:
//服务端
public class Server {
public static void main(String[] args) throws Exception {
//创建服务端并绑定端口
ServerSocket server = new ServerSocket(8888);
System.out.println("服务端已经准备就绪");
String date = "吃个饭再走!!";
//交互信息
boolean accept = true;
while (accept) {
Socket client = server.accept();
PrintStream out = new PrintStream(client.getOutputStream());
out.print(date);
out.close();
}
server.close();
}
}
三、UDP协议解析
UDP协议:用户数据报协议,为应用程序提供了一种无需建立连接就可以发送封装的IP数据包的方法。
1.UDP报文结构
源端口和目标端口:各2个字节的源端口和目标端口用来标记发送和接收的应用进程。因为UDP不需要应答,所以源端口是可选的,若不需要可置为0。
报文长度域:用来指定UDP数据报的长度,最小为1字节
检验和:检验UDP用户数据报在传输中是否有错,有错就丢弃
应用场景:
由于缺乏可靠性且属于非连接导向协议,udp的应用必须允许一定量的丢包、出错和复制粘贴,但是其具有消耗资源小,传输速度快的特点
使用UDP的协议应用有:
域名系统(DNS)
简单网络管理协议(SNMP)
动态主机配置协议(DHCP):该协议允许服务器向客户端动态分配 IP地址和配置信息
2.Java对UDP的支持
Java对UDP协议的支持也是两个类,DatagramSocket与DatagramPacket,DatagramSocket类中提供了数据报包的发送与接收方法,下面是一个非常简单的小例子~
发送端:
public class UDPClient {
public static void main(String[] args) throws Exception {
String date = "苍茫的天涯是我的爱";
DatagramSocket client = new DatagramSocket(10086);
DatagramPacket send = new DatagramPacket(date.getBytes(),
date.getBytes().length, InetAddress.getLocalHost(), 10087);
client.send(send);
client.close();
}
}
接收方:
public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket server = new DatagramSocket(10087);
byte[] buffer = new byte[1024];
DatagramPacket rec = new DatagramPacket(buffer, 1024);
server.receive(rec);
String msg = new String(buffer, 0, buffer.length);
System.out.println(msg);
server.close();
}
}
四、TCP与UDP的区别
TCP与UDP的优劣都是相对而言的;
TCP提供的是面向连接、可靠的字节流服务。当客户端和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能开始数据传输。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端;
UDP是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证数据能够达到目的地,由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
简而言之:
TCP面向连接,可靠,传输速度较慢;
UDP面向数据报,不可靠,传输速度快;
2021-04-23