最近真的比较忙, 很久就想写了, 可是一直苦于写点什么, 今天脑袋灵光一闪, 觉得自己再 UDP 方面还有些不了解的地方, 所以要给自己扫盲.
好了, 咱们进入今天的主题, 先列一下提纲:
1. UDP 是什么, UDP 适用于什么场景?
2. 写一个小 Demo 来加深一下 UDP 的理解.
3. UDP 和 TCP 的区别有哪些?
4. TCP 建连和关闭的过程, 为什么建立连接的时候是三次握手, 断开连接的时候需要四次?
1. UDP 是什么, UDP 适用于什么场景?
相信很多同学都听过 UDP,UDP 的全称: User Datagrame Protocol, 用户报文协议, 是一个传输层协议. UDP 最大的特点是: 不可靠网络传输, 无连接数据协议, 即发送前不要连接, 直接向目标地址发送. 而 TCP 和 UDP 基本上是相互补充的, TCP 是可靠的数据数据传输, 基于连接后的数据发送.
TCP 是 Transmission Control Protocol, 传输控制协议, TCP 是基于可靠的数据传输, 那么就需要牺牲更多的延迟和网络带宽. 而 UDP 则不需要可靠的数据传输, 那么将会需要更小的网络延迟和网络开销. UDP 可以允许丢弃延迟的数据包. 由于低延迟低带宽, 所以 UDP 非常适合电脑游戏, 语音电话, 视频电话, 网络直播.
我们接下来看一下 UDP 的 Packet 的组成 (图片来源网络),8 字节的 Header, 然后就是 UDP 的数据. 本机如果作为客户端的话, 本机的端口号为 0-65535, 也就是本机连接外部机器的话最多可以连接 65536,0 是保留端口号. 如果作为服务端的话, 可以使用的端口为 2 的 32 次方个端口. 也就是可以接收的数据可以有这么多. 当然, 目前一台机器能处理的数据没有这么多.
8 字节的 Header, 很简单也比较少, 不像 TCP 需要 20-60 字节的数据.
Source port, 源端口号, 16 位 2 个字节.
Length, 数据的长度 2 个字节.
Distination port, 目标端口, 用于识别到目标机器的端口号. 2 个字节.
Checksum, 用于计算 Header 的 Checksum(校验值).
2. 写一个小 Demo 来加深一下 UDP 的理解.
1) UDP 的服务端代码, 因为 UDP 的代码都是 JDK 自带的, 所以也不需要引入其他 jar 包就可以.
2)Server 端主要创建步骤:
a) 创建一个监听 udp 的端口号 8888.
b) 创建一个用于接收数据的 DatagramPacket, 参数有两个, 一个是数据, 一个是数据的长度.
c) 采用循环进行 receive 数据, 直到收到的 bye 字符串.
- package myflink.udp;
- import java.io.IOException;
- import java.NET.DatagramPacket;
- import java.NET.DatagramSocket;
- /**
- * @author huangqingshi
- * @Date 2020-05-24
- */
- public class UDPServer {
- public static void main(String[] args) throws IOException {
- //1. 创建一个监听 8888 端口的 udp socket
- DatagramSocket ds = new DatagramSocket(8888);
- // 设置接收数据的最大值
- byte[] receive = new byte[65535];
- // 用于接收的数据
- DatagramPacket datagramPacket = null;
- while(true) {
- //2. 创建一个用于接收数据. buf 即数据和其长度
- datagramPacket = new DatagramPacket(receive,receive.length );
- //3. 接收 byteBuff 的数据
- ds.receive(datagramPacket);
- System.out.println("Client:-" + data(receive));
- //4. 如果接收到了 bye, 程序结束
- if("bye".equals(data(receive))) {
- break;
- }
- //5. 清理 receive 中的数据
- receive = new byte[65535];
- }
- }
- public static StringBuilder data(byte[] bytes) {
- if(bytes == null) {
- return null;
- }
- StringBuilder ret = new StringBuilder();
- int i = 0;
- while (bytes[i] != 0)
- {
- ret.append((char) bytes[i]);
- i++;
- }
- return ret;
- }
- }
3) 接下来是客户端的代码, 步骤如下:
a) 创建 scanner 用于在控制台进行数据输入, 然后创建一个 DatagramSocket 用于处理数据.
b) 创建一个 DatagramPacket 用于数据的发送.
c) 进行数据发送.
d) 持续发送数据, 当收到 bye 字符串的话就会结束.
- package myflink.udp;
- import java.io.IOException;
- import java.NET.DatagramPacket;
- import java.NET.DatagramSocket;
- import java.NET.InetAddress;
- import java.util.Scanner;
- /**
- * @author huangqingshi
- * @Date 2020-05-24
- */
- public class UDPClient {
- public static void main(String[] args) throws IOException {
- Scanner scanner = new Scanner(System.in);
- InetAddress ip = InetAddress.getLocalHost();
- //1. 创建一个 socket 对象用于处理数据
- DatagramSocket socket = new DatagramSocket();
- // 用于存放数据
- byte[] buf = null;
- // 一个死循环, 用于接收数据后处理, 收到 bye 后结束处理
- while(true) {
- String input = scanner.nextLine();
- // 将接收到的信息转换为 byte 数组
- buf = input.getBytes();
- //2. 创建一个 DatagramPack 包用于创建发送的数据
- DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length, ip, 8888);
- //3. 发送数据
- socket.send(datagramPacket);
- //4. 如果收到了 byte 直接结束循环
- if("bye".equals(input)) {
- break;
- }
- }
- }
- }
启动服务端和客户端, 然后再控制台输入一些测试数据, 看一下服务端的控制台输出:
客户端输入数据
Hello UDP
服务端的输出数据
Client:-Hello UDP
好了 Demo 已经执行完了, 非常简单.
3. UDP 和 TCP 的区别有哪些?
1. TCP 是可靠的传输, 而 UDP 是非可靠数据传输. 因为可靠, 所以需要更高的延迟和网络带宽. 而 UDP 则不需要, 所以比较适合语音, 视频电话等.
2. UDP 的 Header 字节为 8 个字节, 非常少, 而 TCP 需要至少 20 个字节.
3. TCP 是全双工的, 即可以发送接收数据, 可以想象两个人打电话, 即可以听到声音又可以发送声音. 而 UDP 发送数据的时候才连接, 发送完数据之后不会保留连接.
4. TCP 是点对点连接的, 而 UDP 是多对多进行数据传输. TCP 以字节流形式发送, 有拥塞控制, 方式发送数据量太大拥塞. 而 UDP 是以报文形式发送给目标机器, 没有拥塞控制.
4. TCP 建连和关闭的过程, 为什么建立连接的时候是三次握手, 断开连接的时候需要四次?
1) 三次握手建立连接处理:
1. 首先创建连接时 client 需要发送一个 SYN + 随机 sequence 给 server 端, 这是客户端的状态是 SYN_SENT 状态.
2. server 收到数据后会回复一个 SYN+ACK,ACK 为接收到的 sequence+1, 同时再发送一个 sequence.server 的状态为 ACK_REVD.
3. client 再把收到的 sequnce+1 作为 ACK 再给到服务端, 然后服务端和客户端的状态都是 ESTABLISHED. 说明连接建立了.
第二步中的 SYN 和 ACK 可以同时发送, 这两个值同时发送不受影响, 都可以建立连接. 当然如果两步分开发送也是可以的, 但是由于可以节省一步发送, 所以不用多费事.
2) 四次握手关闭连接处理:
1. client 发一个 FIN 和一个随机的 sequence 给 server, 然后客户端的状态变为 FIN_WAIT_1 状态.
2. server 收到了 FIN 后, 状态变为 CLOSE_WAIT, 然后再把接收到的 sequence+1 和 ACK 标志返回给 client.client 收到 ACK 后变为 FIN_WAIT2 状态.
3. 然后 server 再次给 client 发送一个 FIN+sequence 给 client, 此时客户端的状态变为 TIME_WAIT 状态.
4. client 再把收到的 sequence + 1 发送给 server, 此时 server 的状态变为 CLOSED. 此时连接正式断开.
这是客户端主动发起关闭连接的过程, 还有同时发送 FIN 标志的情况.
1. client 发送 FIN+sequence 给 server 端, 状态变为 FIN_WAIT_1.
2. server 也发送 FIN+sequence 给 client 端, 此时 server 的状态变为 FIN_WAIT_1.client 的接收 FIN 后变为 CLOSING, 同时 server 也变为 CLOSING.
3. client 发送 ACK + 接收到的 sequence+1 给 server.client 的状态变为 TIME_WAIT.
4. server 同时也发送 ACK + 接收到的 seqnce+1 给 client. 此时 client 和 server 都变为 CLOSED.
整个过程是这么一个过程, 那么为什么 TCP 连接的时候需要三次, 而关闭的时候需要四次?
因为建立连接的时候 SYN+ACK 可以同时发送, 不影响连接建立. 而关闭的时候 FIN+ACK 不能合起来, 因为 TCP 是双向且全双工连接. 也就是 client 和 server 建立好连接后, client 和 server 即能发送信息同时也能接收信息. 当 client 发送 FIN 给 server 的时候, 只能说明客户端不给 server 发送数据了, 但是不证明 client 不接收数据, 所以给到 server 后, server 处理好之后说我也不给你发数据了 (FIN). 然后我已经你不给我发数据了 (ACK), 这个时候 client 收到后说知道了 (ACK), 此时连接就关闭了.
好了, 关于这篇就整理到这里, 如果有不对的地方欢迎批评指正.
来源: https://www.cnblogs.com/huangqingshi/p/12950639.html