UDP是什么
UDP是一个传输层的数据包装协议,特点有:
- 无连接(不需要两端连通就可以发送消息给接收端)
 - 不可靠传输(无法知道数据是否送达)
 - 面向数据报(以数据报为数据传输单位)
 - 全双工(两端都可发送可接收)
 - 发送数据的大小受限(64KB的缓冲区)
 
先简单了解一下UDP协议的格式:

协议报头组成大概就是:
源端口号 + 目的端口号 + UDP的长度 + UDP校验和
每一项都是用16bits(2字节)来存储,总共报头大小为64bits也就是8字节。
因为有了16bits的UDP长度限制,所以就是限制了数据的内容不能超过16bits,即64KB的大小,假如有更大的数据就只能拆成多个数据报来传输,这个过程可能会出现数据丢失等问题。所以发送数据的大小受限是UDP协议的一个缺点。
如何使用UDP协议进行传输
使用UDP传输的基本操作有:
- 传输通过DatagramSocket在两个进程之间通讯,数据通过DatagramPacket打包。
 - DatagramSocket的参数通常为Port
 - DatagramPacket的参数通常为byte[]和byte[]的长度(将字节数组打包到packet中)
 - 主机地址由IP决定,进程由端口号(PORT)决定
 - 接收端只需要绑定固定的端口号,发送端需要知道接收端的端口号和IP(主机的保留域名为localhost,或者127.0.0.1)
 - 创建Socket地址通过InetSocketAddress API,
 
如何编写最简单的UDP代码
有了上述基本操作我们可以写出对最基本的UDP客户端服务器:
对服务器有:
public class UdpEchoServer {
    private static final int PORT = 8888;
    public static void main(String[] args) throws IOException {
        //创建一个DatagramSocket数据报套接字,用于在进程间通讯
        DatagramSocket socket = new DatagramSocket(PORT);
        while (true) {
            //创建bytes[]来获取数据socket中的内容
            //创建DatagramPacket来打包数据(对服务器而言是获取DatagramSocket中的数据)
            byte[] bytes = new byte[516];
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
            
            //阻塞等待数据报接收
            System.out.println("等待接收UDP数据报...");
            socket.receive(packet);
            //这里执行完后其实已经完成了接收工作
            //可以调用一些方法来查看获取的数据、数据来源等
            System.out.println("客户端IP:" + packet.getSocketAddress());
            System.out.println("客户端Port:" + packet.getPort());
            System.out.println("客户端发送的原数据:" + packet.getData());
        }
    }
}
 
  对客户端有:
public class UdpClient {
    private static final SocketAddress address =
        new InetSocketAddress("127.0.0.1", 8888);
    public static void main(String[] args) throws IOException {
        //创建DatagramPacket用于打包数据
        DatagramSocket socket = new DatagramSocket();
        byte[] bytes = "miss everyday".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address);
        socket.send(packet);
        System.out.println("客户端数据已发送...");
    }
} 
  注:对服务器而言,传入PORT就是告诉Socket从这个端口接收数据。对客户端而言,不需要告诉Socket从哪里接收数据,但是需要告诉Socket把数据送去哪个端口,所以把SocketAddress给写入了packet中,由packet去告诉Socket往哪个端口送数据。
知道了如何写出基本的UDP协议下的数据传输后,我们可以稍微升级一下,写一个翻译服务器,即在客户端发送翻译请求,然后服务器返回发送的结果。
对服务器有:
public class UdpDictClient {
    private static final SocketAddress address =
            new InetSocketAddress("localhost", 4090);
    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            byte[] resp = new byte[128];
            DatagramPacket packet = new DatagramPacket(resp, resp.length);
            System.out.print("请输入要翻译的英文(以.结尾):");
            byte[] req = scanner.next().getBytes();
            DatagramPacket request = new DatagramPacket(req, req.length, address);
            socket.send(request);
            System.out.println("翻译请求已发送...");
            socket.receive(packet);
            String answer = new String(packet.getData());
            System.out.println("结果:" + answer);
        }
    }
} 
  对客户端有:
public class UdpDictServer {
    private static HashMap<String, String> dict;
    private static final int PORT = 4090;
    public static void main(String[] args) throws IOException {
        iniDict();
        System.out.println("服务器启动...");
        DatagramSocket socket = new DatagramSocket(PORT);
        while (true) {
            System.out.println("正在等待接收客户端请求...");
            byte[] req = new byte[128];
            DatagramPacket request = new DatagramPacket(req, req.length);
            socket.receive(request);
            
            System.out.println("已收到客户端" + 
                request.getSocketAddress() + "请求,开始处理...");
            String getBytes = new String(request.getData());
            char[] chars = getBytes.toCharArray();
            StringBuilder stringBuilder = new StringBuilder();
            for (char aChar : chars) {
                if (aChar == '.') {
                    break;
                } else {
                    stringBuilder.append(aChar);
                }
            }
            getBytes = stringBuilder.toString();
            String resp = dict.getOrDefault(getBytes, "没有找到翻译");
            byte[] bytes = resp.getBytes();
            DatagramPacket response =
                    new DatagramPacket(bytes, bytes.length, request.getSocketAddress());
            socket.send(response);
            System.out.println("已向客户端返回响应...");
            System.out.println("------------------");
        }
    }
    private static void iniDict () {
        dict = new HashMap<>();
        dict.put("Matty", "Lyh");
        dict.put("Justin", "比伯");
        dict.put("Stephen", "库里");
    }
}
 
  这样下来应该就对UDP有一定认识了



















