(四) Socket——UDP快速入门

1、UDP是什么

  • Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议,又称用户数据报文协议,基于报文的协议(不同于TCP面向链接的协议)
  • 是一种简单的面向数据报传输层协议,正式规范RFC 768,提供面向事务的简单不可靠信息传送服务

为什么不可靠

  • 一旦把应用程序发给网络层的数据发送出去,就不保留数据备份
  • UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)
  • 发送端是产生数据,在接收端从网络中抓取数据
  • 特点:结构简单、无校验、速度快、容易丢包、可广播

UDP能做什么

  • DNS、TFTP、SNMP
  • 视频、音频、普通数据(无关紧要数据)

2、UDP核心API讲解

API-DatagramSocket

  • 此类表示用来发送和接收数据包的套接字,数据报套接字是包投递服务的发送或接收点
  • 负责发送某一个UDP包,或者接收UDP包
  • 不同于TCP,UDP并没有合并到Socket API中(UDP中没有区分客户端和服务器,DatagramSocket即是客户端也是服务端,可以发送也可以接收,不需要连接)
  • DatagramSocket()创建简单实例,不指定端口与IP(使用它发送,他会自动赋予本地可用端口进行发送数据)
  • DatagramSocket( int port)创建监听固定端口的实例
  • DatagramSocket( int port,InetAddress localAddr)创建固定端口制定IP的实例

3、UDP单播、广播、多播

广播地址

4、局域网搜索案——A向B发送信息

  • 4.1、实现UDP接收消息并回送功能

UDP提供者代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
 * UDP提供者,用于提供服务
 */
public class UDPprovider {
    public static void main(String[] args) throws IOException {
        System.out.println("UDPProvider started.");
        //作为接受者,制定一个端口用于数据接收
        DatagramSocket ds = new DatagramSocket(20000);
        //构建接受实体
        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+"\tport:"+port+"\tdata:"+data);
        //构建一份回送数据
        String responseData="\nreceive data with len:"+dataLen;
        //构建回送消息的实体
        byte[] responseDataBytes=responseData.getBytes();
        //直接根据发送者构建一份回送信息
        DatagramPacket responsePacket =new DatagramPacket(responseDataBytes,
                responseDataBytes.length,
                receivePack.getAddress(),
                receivePack.getPort());
        ds.send(responsePacket);
        //完成
        System.out.println("UDPProvider finished.");
        ds.close();
    }
}

UDP搜索者代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
 * UDP搜索者用于搜索支持方
 */
public class UDPsearcher {
    public static void main(String[] args) throws IOException {
        System.out.println("UDPseacher started.");
        //作为搜索方,让系统自动分配端口
        DatagramSocket ds = new DatagramSocket();
        //构建一份请求数据
        String requestData="helloworld!";
        //构建回送消息的实体
        byte[] requestDataBytes=requestData.getBytes();
        //直接构建packet
        DatagramPacket requestPacket =new DatagramPacket(requestDataBytes,requestDataBytes.length);
        //本机端口20000
        requestPacket.setAddress(InetAddress.getLocalHost());
        requestPacket.setPort(20000);
        //发送
        ds.send(requestPacket);
        //构建接受实体
        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("UDPseacher receive form ip:"+ip+"\tport:"+port+"\tdata:"+data);
        //完成
        System.out.println("UDPseacher finished.");
        ds.close();
    }
}

5、局域网搜索案例——A向局域网内所有设备发送信息

  • UDP局域网广播发送的实现
  • UDP局域网回送消息的实现
  • A作为提供者,B作为接收者

UDP提供者,用于提供服务

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.UUID;
/**
 * UDP提供者,用于提供服务
 */
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) {
            super();
            this.sn = sn;
        }
        //Intellij Idea get/set/Override方法快捷键Alt+Insert,或直接ctrl+o
        @Override
        public void run() {
            super.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 + "\tport:" + port + "\tdata:" + 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 e){ //这个异常可以忽略
            }finally {
                close();
            }
            //完成
            System.out.println("UDPProvider finished.");
        }
        private  void close(){//socket进行结束操作
            if(ds!=null){
                ds.close();
                ds=null;
            }
        }
        /**
         * 提供结束
         */
        void exit(){
            done=true;
            close();
        }
    }
}

消息创建者,具备两个口令,一个搜索口令,一个回送口令

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搜索者用于搜索支持方

import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
 * UDP搜索者用于搜索支持方
 */
public class UDPsearcher {
    private  static  final int LISTEN_PORT=30000;//定义回送监听端口号
    public static void main(String[] args) throws IOException, InterruptedException {
         System.out.println("UDPseacher started.");
        Lisener listener = listen();//开始一个监听
        sendBroadcast();//监听之后发送一个广播
       /* //作为搜索方,让系统自动分配端口
        DatagramSocket ds = new DatagramSocket();
        //构建一份请求数据
        String requestData="helloworld!";
        //构建回送消息的实体
        byte[] requestDataBytes=requestData.getBytes();
        //直接构建packet
        DatagramPacket requestPacket =new DatagramPacket(requestDataBytes,requestDataBytes.length);
        //本机端口20000
        requestPacket.setAddress(InetAddress.getLocalHost());
        requestPacket.setPort(20000);
        //发送
        ds.send(requestPacket);
        //构建接受实体
        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("UDPseacher receive form ip:"+ip+"\tport:"+port+"\tdata:"+data);*/
        //ds.close();
        //读取任意键盘信息后可以退出
        System.in.read();
        List<Device> devices = listener.getDevicesAndClose();//结束之前拿到设备信息,从监听器中拿我们的设备信息
        for (Device device : devices) {//拿到设备之后,打印设备信息
            System.out.println("Device:"+device.toString());
        }
        //完成
        System.out.println("UDPseacher finished.");
    }
    //监听方法
    private static Lisener listen() throws InterruptedException {//监听方法
        System.out.println("UDPseacher start listen.");
        CountDownLatch countDownLatch=new CountDownLatch(1);
        Lisener lisener=new Lisener(LISTEN_PORT,countDownLatch);
        lisener.start();//启动操作
        countDownLatch.await();//等待启动完成
        return lisener;
    }
    //发送广播的方法
    public static void sendBroadcast() throws IOException {//发送广播的方法
        System.out.println("UDPseacher 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("UDPseacher sendBroadcast finished.");
    }
    private  static  class Device{//设备类信息
        final int port;//端口
        final String ip;
        final String sn;
        //三者都是必须要的东西,因此加上final
        private Device(int port, String ip, String sn) {
            this.port = port;
            this.ip = ip;
            this.sn = sn;
        }
        //同时构建一个tostring方法:快捷键Alt+Insert选择tostring()再点击OK
        @Override
        public String toString() {
            return "Device{" +
                    "port=" + port +
                    ", ip='" + ip + '\'' +
                    ", sn='" + sn + '\'' +
                    '}';
        }
    }
    private  static  class  Lisener extends  Thread{
        private  final  int listenPort;//需要传入的端口号
        private  final CountDownLatch countDownLatch;//让外面感知到已经开始监听了
        private  final List<Device> devices=new ArrayList<>();//设备链表
        private  boolean done=false;//结束符,默认为false
        private  DatagramSocket ds=null;//搜索者
        public Lisener(int listenPort, CountDownLatch countDownLatch) {
            super();
            this.listenPort = listenPort;
            this.countDownLatch = countDownLatch;
        }
        @Override
        public void run() {
            super.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("UDPseacher receive form ip:"+ip+"\tport:"+port+"\tdata:"+data);
                    //解析操作,解析sn
                    String sn=MessageCreator.parseSn(data);
                    if(sn!=null){
                        Device device=new Device(port,ip,sn);//传入新设备端口号、ip及sn信息
                        devices.add(device);//sn解析成功,设备链表增加一个新的设备
                    }
                }
            }catch (Exception e){
            }finally {
                close();
            }
            System.out.println("UDPseacher listener finished");
        }
        private  void close(){
            if(ds!=null){
                ds.close();
                ds=null;
            }
        }
        List<Device>getDevicesAndClose(){//拿并关闭的操作
            done=true;
            close();
            return devices;
        }
    }
}

运行结果:
首先要打开提供方,再打开搜索方
在这里插入图片描述在这里插入图片描述
在搜索方输入任意字符结束搜索和提供方:
在这里插入图片描述在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值