UDP 理论详解

UDP 简 介

  • UDP 是一种高速,无连接的数据交换方式UDP 传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,所以说 UDP 是无连接的、不可靠的一种数据传输协议
  • UDP( User Datagram Protocol ) 用户数据报协议,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
  • 因为不必进行收发数据的确认,所以 UDP 的实时性更好,传输速率更高。有时候速度比数据完整性重要,在比如视频会议、电话通信等,丢失几帧画面、声音是可以接受的,但在需要数据安全接受的环境下,就宜采用TCP
  • 在网络质量令人不十分满意的环境下,UDP 协议数据包丢失会比较严重。但是由于它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
  • Java 中使用 UDP 编程主要使用 java.net 包下的 DatagramSocket 和 DatagramPacket 类
  • 可以参考《TCP 理论详解

通信示例

·接收端·

  • 使用 DatagramSocket(int port) 建立socket(套间字)服务,必须指定监视的 port
  • 定义数据报包(DatagramPacket),用于储存接收到的数据
  • 通过 DatagramSocket 的 receive 方法将接收的数据存入上面定义的包中
  • 使用 DatagramPacket 的 getData 方法,提取数据
  • DatagramPacket 关闭资源
package com.lct.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.Date;

/**
 * Created by Administrator on 2018/10/14 0014.
 * UDP 接收端
 */
public class UdpServer {
    public static void main(String[] args) {
        udpReceive();
    }

    /**
     * Udp 监听端口数据
     */
    public static void udpReceive() {
        DatagramSocket datagramSocket = null;
        /** 数据接收大小设置为1024字节,超出部分是接收不到的
         */
        byte[] buffer = new byte[1024];
        DatagramPacket datagramPacket;
        try {
            /**
             * DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。
             * DatagramSocket(int port):创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、指定端口。
             * */
            datagramSocket = new DatagramSocket(8080);
            datagramPacket = new DatagramPacket(buffer, buffer.length);

            /**
             * setSoTimeout(int timeout):设置 DatagramSocket 的 receive(DatagramPacket p) 方法
             * 阻塞监听端口时的超时时间,单位毫秒,
             * 当 receive 方法阻塞监听端口超过指定的时间时,抛出异常:java.net.SocketTimeoutException: Receive timed out
             */
            /*datagramSocket.setSoTimeout(60 * 1000);*/

            /** 接收端循环监听*/
            while (true) {
                System.out.println(new Date() + " :开始监听端口" + Thread.currentThread().getName());
                /**阻塞监听端口*/
                datagramSocket.receive(datagramPacket);
                /**读取数据*/
                String message = new String(datagramPacket.getData(), 0, datagramPacket.getLength(), Charset.forName("UTF-8"));
                System.out.println("监听到数据:" + message);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            /**如果发送异常,关闭操作*/
            if (datagramSocket != null) {
                if (!datagramSocket.isConnected()) {
                    datagramSocket.disconnect();
                }
                if (!datagramSocket.isClosed()) {
                    datagramSocket.close();
                }
            }
        }
    }
}

·发送端·

  • 使用 DatagramSocket 建立 socket(套间字)服务,可以指定端口,也可让它自动分配
  • 将数据打包到 DatagramPacket 中
  • 通过 socket 服务发送打包好的 DatagramPacket(send() 方法
  • socket 服务关闭资源
package com.lct.udp;

import java.io.IOException;
import java.net.*;

/**
 * Created by Administrator on 2018/10/14 0014.
 * Udp 发送端
 */
public class UcpClient {
    public static void main(String[] args) {
        /**
         * 使用 3 个线程发送消息,模拟 多个客户端操作
         */
        for (int i = 0; i < 3; i++) {
            new Thread() {
                @Override
                public void run() {
                    sendUdpMessage();
                }
            }.start();
        }
    }

    /**
     * 发送 Udp 消息
     */
    public static void sendUdpMessage() {
        DatagramSocket datagramSocket = null;
        try {
            /** 实例化数据报套接字*/
            datagramSocket = new DatagramSocket();

            /**将被发送的数据转为字节数组,同时指定编码*/
            String msg = "修长城的民族!" + Thread.currentThread().getName();
            byte[] msgBytes = msg.getBytes("UTF-8");

            /** InetAddress:指定 UDP 接收端的 IP 地址*/
            InetAddress inetAddress = InetAddress.getByName("192.168.1.20");

            /**
             * DatagramPacket(byte buf[], int length,InetAddress address, int port)
             *      buf[]:被发送的数据
             *      length:指定 buf[] 中 [0,length]的数据进行发送
             *      address:udp 接收端的地址
             *      port:udp 接收端监听的端口
             */
            DatagramPacket datagramPacket = new DatagramPacket(msgBytes, msgBytes.length, inetAddress, 8080);
            /**发送数据*/
            datagramSocket.send(datagramPacket);
            System.out.println("消息发送结束..............." + Thread.currentThread().getName());
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            /**操作结束,释放 datagramSocket,并关闭*/
            if (datagramSocket != null) {
                if (!datagramSocket.isConnected()) {
                    datagramSocket.disconnect();
                }
                if (!datagramSocket.isClosed()) {
                    datagramSocket.close();
                }
            }
        }
    }
}

udp 接收端输出如下:

Sun Oct 14 11:43:40 CST 2018 :开始监听端口main
监听到数据:修长城的民族!Thread-0
Sun Oct 14 11:43:42 CST 2018 :开始监听端口main
监听到数据:修长城的民族!Thread-1
Sun Oct 14 11:43:42 CST 2018 :开始监听端口main
监听到数据:修长城的民族!Thread-2
Sun Oct 14 11:43:42 CST 2018 :开始监听端口main

udp 发送端输出如下:

消息发送结束...............Thread-1
消息发送结束...............Thread-0
消息发送结束...............Thread-2

网络编程

  • 技术日新月异,目前网络编程最为流行的当属 Netty,Java 网络编程发展历程:

JDK 1.4 以前:java.net + java.io——即平时所使用的简单的 TCP 、UDP 编程

JDK 1.4 及以后:java.nio

当下流行:JBoos 的 Netty 库、Apache 的  Mina 等

UDP 理论详解