1. UDP通信原理

UDP协议的特点

  1. UDP是一种无连接、不可靠传输协议

  2. 将数据源IP、目的地IP和端口以及数据封装成数据包,大小限制在64KB之内,直接发送出去即可

  3. 和tcp同属于传输层的一种传输协议,常用于消息接发等需要安全性较低的场景

  4. 使用udp通信需具备客户端和服务端,服务端要先于客户端启动

UDP协议只实现了两个功能

  1. 在IP协议的基础上添加了端口

  2. 对传输过程中可能产生的数据错误进行了检测,并抛弃已经损坏的数据

2. 关键API

java.net包里提供了实现网络应用程序的类

在Java中,我们可以使用DatagramSocket来创建UDP服务器,使用DatagramPacket来装载接受的数据

DatagramSocket这个类是用来发送和接收数据包的套接字类

发送数据包用的方法是: DatagramSocket.send(DatagramPacket p)
接收数据包用的方法是: DatagramSocket.receive(DatagramPacket p)

DatagramPacket这个类是用来存储数据的数据包,一般的用法如下:

//其中data是数据字节数组,address是目标地址,port是目标端口号
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);

3. UDP报文格式

首先要知道UDP是一种无连接的传输层协议。与TCP不同的是,UDP不保证数据完整无误的发送给目标,而是通过IP层的最大努力机制来传递数据的。

每个UDP报文分别由UDP报文头和UDP数据区两部分组成。

其中UDP报文头有8个字节:

源端口(2字节)+目的端口(2字节)+报文长度(2字节)+校验值(2字节)

UDP数据区长度可变,根据报文长度不同而不同。

4. UDP的通信建立的步骤

UDP客户端首先向被动等待联系的服务器发送一个数据报文。一个典型的UDP客户端要经过下面三步操作:

  1. 创建一个DatagramSocket实例,可以有选择地对本地地址和端口号进行设置,如果设置了端口号,则客户端会在该端口号上监听从服务器端发送来的数据

  2. 使用DatagramSocket实例的send()和receive()方法来发送和接收DatagramPacket实例,进行通信

  3. 通信完成后,调用DatagramSocket实例的close()方法来关闭该套接字

由于UDP是无连接的,因此UDP服务端不需要等待客户端的请求以建立连接。另外,UDP服务器为所有通信使用同一套接字,这点与TCP服务器不同,TCP服务器则为每个成功返回的accept()方法创建一个新的套接字。一个典

型的UDP服务端要经过下面三步操作:

  1. 创建一个DatagramSocket实例,指定本地端口号,并可以有选择地指定本地地址,此时,服务器已经准备好从任何客户端接收数据报文

  2. 使用DatagramSocket实例的receive()方法接收一个DatagramPacket实例,当receive()方法返回时,数据报文就包含了客户端的地址,这样就知道了回复信息应该发送到什么地方

  3. 使用DatagramSocket实例的send()方法向服务器端返回DatagramPacket实例

5. UDP通信案例

5.1 服务器端代码

package Udp通信;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UdpServer {
    public static void main(String[] args) throws Exception {
        // 1.创建服务器端:注册端口
        DatagramSocket socket = new DatagramSocket(3333);

        // 2.创建一个数据包对象接收客户端发送的数据
        // 创建字节数组,指定接收的数据包的大小
        byte[] buf = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        // 3.服务器启动成功,输出提示信息
        System.out.println("****服务器端启动成功****");

        // 4.通过循环不停的向客户接收数据
        new Thread(() -> {
            while (true) {
                try {
                    // 4.1 等待接收数据,此方法在接收到数据报之前会一直阻塞
                    socket.receive(packet);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // 4.2 读取数据
                int len = packet.getLength();
                String rs = new String(buf, 0, len);
                System.out.println("收到了ip为:" + packet.getAddress() + " 端口号为:" + packet.getPort() + "的消息:" + rs);
            }
        }).start();

        final InetAddress[] address = {null};
        final int[] port = {0};
        final boolean[] flag = {false};
        // 5.向客户端发送信息
        new Thread(() -> {
            while (true) {
                if (!flag[0]) {
                    // 5.1 定义客户端的地址、端口号、数据
                    address[0] = packet.getAddress();//获取发送端的地址
                    port[0] = packet.getPort();//获取 发送端进程所绑定的端口
                    flag[0] = true;
                }
                System.out.println("请输入:");
                // 5.2 从键盘接受数据
                Scanner scanner = new Scanner(System.in);
                //nextLine方式接受字符串
                String msg = scanner.nextLine();
                // 5.3 创建一个数据包对象封装数据
                byte[] buffer = msg.getBytes();
                // 5.4 创建数据报,包含响应的数据信息
                DatagramPacket packets = new DatagramPacket(buffer, buffer.length, address[0], port[0]);
                try {
                    // 5.5 发送数据给客户端
                    socket.send(packets);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

5.2 客户端代码

package Udp通信;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UdpClient {
    public static void main(String[] args) throws Exception {
        // 1.定义服务器的地址、端口号、数据
        InetAddress address = InetAddress.getByName("localhost");
        // 2.定义服务器端口
        int port = 3333;

        // 3.创建发送端对象:发送端自带默认端口号
        DatagramSocket socket = new DatagramSocket(2222);

        // 4.客户端启动成功,输出提示信息
        System.out.println("****客户端启动成功****");

        // 5.向服务端发送信息
        new Thread(() -> {
            try {
                while (true) {
                    System.out.println("请输入:");
                    // 5.1 从键盘接受数据
                    Scanner sc = new Scanner(System.in);
                    // 5.2 nextLine方式接受字符串
                    String msg = sc.nextLine();
                    // 5.3 创建一个数据包对象封装数据
                    byte[] buffer = msg.getBytes();
                    DatagramPacket packets = new DatagramPacket(buffer, buffer.length, address, port);
                    // 5.4 发送数据出去
                    socket.send(packets);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        // 6.通过循环不停的向服务器接收数据
        new Thread(() -> {
            try {
                while (true) {
                    // 6.1 创建一个数据包对象接收数据
                    byte[] buf = new byte[1024 * 64];
                    DatagramPacket packet = new DatagramPacket(buf, buf.length);
                    // 6.2 接收服务器响应的数据
                    socket.receive(packet);
                    // 6.3 取出数据
                    int len = packet.getLength();
                    String rs = new String(buf, 0, len);
                    System.out.println("收到了ip为:" + packet.getAddress() + " 端口号为:" + packet.getPort() + "的消息:" + rs);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
}

6. 通信测试

6.1 启动服务器

6.2 启动客户端

6.3 客户端给服务端发送信息,服务端收到信息

6.4 服务器给客户端发送信息,客户端收到信息

版权声明:本文为helloliyh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/liyhbk/p/16451038.html