Socket - UDP疾速入门

UDP是什么

  • 英语:User Datagram Protocol,缩写为UDP。
  • 一种用户数据报协定,又称用户数据报文协定
  • 是一个简略的面向数据报传输层协定,正式标准为 RFC 768。
  • 用户数据协定、非连贯协定。

为什么UDP是不牢靠的

  • 它一旦把应用程序发给网络层的数据发送进来,就不保留数据备份。
  • UDP在IP数据报的头部仅仅退出了复用和数据校验字段。
  • 发送端生产数据,接收端从网络中抓取数据。
  • 构造简略、无校验、速度快、容易丢包、可播送。

UDP能做什么

UDP是面向音讯的协定,通信时不须要建设连贯,数据的传输天然是不牢靠的,UDP个别用于多点通信和实时的数据业务,比方:

  • 语音播送
  • 视频
  • QQ
  • TFTP(简略文件传送)
  • SNMP(简略网络管理协定)
  • RIP(路由信息协定,如报告股票市场,航空信息)
  • DNS(域名解释)
  • 重视速度晦涩

UDP操作简略,而且仅须要较少的监护,因而通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议零碎,并不要求音频视频数据相对的正确,只有保障连贯性就能够了,这种状况下显然应用UDP会更正当一些。

UDP通信模型

UDP通信模型中,在通信开始之前,不须要建设相干的链接,只须要发送数据即可,相似于生存中,"写信""

外围API解说

DatagramSocket

  • 用于接管与发送UDP的类。
  • 负责发送某一个UDP包,或者承受UDP包。
  • 不同于TCP,UDP并没有合并到 Socket API 中。
  • DatagramSocket() 创立简略实例,不指定端口与IP。
  • DatagramSocket(int port) :创立监听固定端口的实例。
  • DatagramSocket(int port , InetAddress localAddr ) :创立固定端口指定IP的实例。
  • receive(DatagramPacket d):接管。
  • send(DatagramPacket d):发送。
  • setSoTimeout(int timeout):设置超时工夫,毫秒。
  • close():敞开,开释资源。

DatagramPacket

  • 用于解决报文
  • 将byte数组、指标地址、指标端口等数据包装成报文或者将报文装配成byte数组。
  • DatagramPacket是UDP的发送实体,也是承受实体。
  • DatagramPacket(byte[] buf , int offset , int length , InetAddress address , int port ) :指定byte数组和应用区间,后两个指定指标机器地址与端口。
  • DatagramPacket(byte[] buf , int length , SocketAddress address) :SocketAddress相当于InetAddress + Port。
  • setData(byte[] buf , int offset , int length):传入byte数据和长度信息。
  • setData(byte buf):传入整个byte数组的信息。
  • seLength(int length):设置无效区间。
  • getData() 、getOffset() 、getLength() :返回对应上述信息。
  • setAddress(InetAddress addr):指标地址。
  • setPort(int port):指标端口。
  • 对应的getAddress()、getPort()。
  • setSocketAddress(SocketAddress address):传入InetAddress + Port。
  • getSocketAddress():获取socket地址。

UDP 单播、播送、多播

  • 单播:用于两个主机之间的端对端通信。
  • 播送:用于一个主机对整个局域网上所有的主机上的数据通信。
  • 多播:对一组特定的主机进行通信,而不是整个局域网上的所有主机。

IP地址

  • A类:前8位示意网络ID,后24位示意主机ID;该地址调配给政府机关单位应用。
  • B类:前16位示意网络ID,后16位示意主机ID;该地址调配给中等规模的企业应用。
  • C类:前24位示意网络ID,后8位示意主机ID;该地址调配给任何须要的人应用。
  • D类:不分网络ID和主机ID;该地址用于多播。
  • E类:不分网络ID和主机ID;该地址用于试验。

地址辨别

IP地址被分类当前,如何判断一个IP地址是A类、B类还是C类地址呢?为了更好地进行辨别,将每类地址的结尾局部设置为固定数值。如下:

从图中能够看出,每类IP地址都是以32位的二进制格局显示的,每类地址的区别如下:

  • A类:网络ID的第一位以0开始的地址。
  • B类:网络ID的第一位以10开始的地址。
  • C类:网络ID的第一位以110开始的地址。
  • D类:地址以1110开始的地址。
  • E类:地址以11110开始的地址。

地址范畴

因为每类地址的结尾是固定的,因而每类地址都有本人的范畴:

  • A类:IP地址范畴为0.0.0.0~127.255.255.255。
  • B类:IP地址范畴为128.0.0.0~191.255.255.255。
  • C类:IP地址范畴为192.0.0.0~223.255.255.255。
  • D类:IP地址范畴为224.0.0.0~239.255.255.255。
  • E类:IP地址范畴为240.0.0.0~255.255.255.254。

播送地址

  • 255.255.255.255 为受限播送地址,如果向该地址的2000端口号发送一个信息,其实只有局域网内的设施可能收到。
  • C 网播送地址个别为:XXX.XXX.XXX.255(192.168.1.255)。
  • D类地址个别为多播预留。

IP地址形成

IP地址形成是应用短整型来存储的。一共四位,也就是一个int值。

局域网搜寻代码案例

/** *  * 音讯构建 * @author Jack */public class MessageCreator {    private static final String SN_HEADER = "收到暗号,我是(SN):";    private static final String PORT_HEADER = "这是暗号,请回电端口(Port):";    public static String buildWithPort(int port) {        return PORT_HEADER + port;    }    public static int parsePort(String data) {        if (data.startsWith(PORT_HEADER)) {            return Integer.parseInt(data.substring(PORT_HEADER.length()));        }        return -1;    }    public static String buildWithSn(String sn) {        return SN_HEADER + sn;    }    public static String parseSn(String data) {        if (data.startsWith(SN_HEADER)) {            return data.substring(SN_HEADER.length());        }        return null;    }}
/** * UDP 提供者,用于提供服务 * @author Jack */public class UDPProvider {    public static void main(String[] args) throws IOException {        // 生成一份惟一标示        String sn = UUID.randomUUID().toString();        Provider provider = new Provider(sn);        provider.start();        // 读取任意键盘信息后能够退出        System.in.read();        provider.exit();    }    private static class Provider extends Thread {        private final String sn;        private boolean done = false;        private DatagramSocket ds = null;        public Provider(String sn) {            this.sn = sn;        }        @Override        public void run() {            System.out.println("UDPProvider Started.");            try {                // 监听20000 端口                ds = new DatagramSocket(20000);                while (!done) {                    // 构建接管实体                    final byte[] buf = new byte[512];                    DatagramPacket receivePack = new DatagramPacket(buf, buf.length);                    // 接管                    ds.receive(receivePack);                    // 打印接管到的信息与发送者的信息                    // 发送者的IP地址                    String ip = receivePack.getAddress().getHostAddress();                    int port = receivePack.getPort();                    int dataLen = receivePack.getLength();                    String data = new String(receivePack.getData(), 0, dataLen);                    System.out.println("UDPProvider receive form ip:" + ip + ", port:" + port + ", data:" + data);                    // 解析端口号                    int responsePort = MessageCreator.parsePort(data);                    if (responsePort != -1) {                        // 构建一份回送数据                        String responseData = MessageCreator.buildWithSn(sn);                        byte[] responseDataBytes = responseData.getBytes();                        // 间接依据发送者构建一份回送信息                        DatagramPacket responsePacket = new DatagramPacket(responseDataBytes, responseDataBytes.length, receivePack.getAddress(), responsePort);                        ds.send(responsePacket);                    }                }            } catch (Exception ignored) {            } finally {                close();            }            // 实现            System.out.println("UDPProvider Finished.");        }        private void close() {            if (ds != null) {                ds.close();                ds = null;            }        }        /**         * 提供完结         */        void exit() {            done = true;            close();        }    }}
/** * UDP 搜寻者,用于搜寻服务反对方 * @author Jack */public class UDPSearcher {    private static final int LISTEN_PORT = 30000;    public static void main(String[] args) throws IOException, InterruptedException {        System.out.println("UDPSearcher Started.");        Listener listener = listen();        sendBroadcast();        System.in.read();        List<Device> devices = listener.getDevicesAndClose();        for (Device device : devices) {            System.out.println("Device:" + device.toString());        }        // 实现        System.out.println("UDPSearcher Finished.");    }    private static Listener listen() throws InterruptedException {        System.out.println("UDPSearcher start listen.");        CountDownLatch countDownLatch = new CountDownLatch(1);        Listener listener = new Listener(LISTEN_PORT, countDownLatch);        listener.start();        countDownLatch.await();        return listener;    }    private static void sendBroadcast() throws IOException {        System.out.println("UDPSearcher sendBroadcast started.");        // 作为搜寻方,让零碎主动调配端口        DatagramSocket ds = new DatagramSocket();        // 构建一份申请数据        String requestData = MessageCreator.buildWithPort(LISTEN_PORT);        byte[] requestDataBytes = requestData.getBytes();        // 间接构建packet        DatagramPacket requestPacket = new DatagramPacket(requestDataBytes,                requestDataBytes.length);        // 20000端口, 播送地址        requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));        requestPacket.setPort(20000);        // 发送        ds.send(requestPacket);        ds.close();        // 实现        System.out.println("UDPSearcher sendBroadcast finished.");    }    private static class Device {        final int port;        final String ip;        final String sn;        private Device(int port, String ip, String sn) {            this.port = port;            this.ip = ip;            this.sn = sn;        }        @Override        public String toString() {            return "Device{" +                    "port=" + port +                    ", ip='" + ip + '\'' +                    ", sn='" + sn + '\'' +                    '}';        }    }    private static class Listener extends Thread {        private final int listenPort;        private final CountDownLatch countDownLatch;        private final List<Device> devices = new ArrayList<>();        private boolean done = false;        private DatagramSocket ds = null;        public Listener(int listenPort, CountDownLatch countDownLatch) {            this.listenPort = listenPort;            this.countDownLatch = countDownLatch;        }        @Override        public void run() {            // 告诉已启动            countDownLatch.countDown();            try {                // 监听回送端口                ds = new DatagramSocket(listenPort);                while (!done) {                    // 构建接管实体                    final byte[] buf = new byte[512];                    DatagramPacket receivePack = new DatagramPacket(buf, buf.length);                    // 接管                    ds.receive(receivePack);                    // 打印接管到的信息与发送者的信息                    // 发送者的IP地址                    String ip = receivePack.getAddress().getHostAddress();                    int port = receivePack.getPort();                    int dataLen = receivePack.getLength();                    String data = new String(receivePack.getData(), 0, dataLen);                    System.out.println("UDPSearcher receive form ip:" + ip + "\tport:" + port + "\tdata:" + data);                    String sn = MessageCreator.parseSn(data);                    if (sn != null) {                        Device device = new Device(port, ip, sn);                        devices.add(device);                    }                }            } catch (Exception ignored) {            } finally {                close();            }            System.out.println("UDPSearcher listener finished.");        }        private void close() {            if (ds != null) {                ds.close();                ds = null;            }        }        List<Device> getDevicesAndClose() {            done = true;            close();            return devices;        }    }}