Android广播--实现基于UDP协议通信

一、什么是UDP协议

我们最常听到的就是TCP与UDP的区别,首先要明白TCP/IP网络模型,TCP/IP是互联网相关的各类协议族的总称,之所以命名为tcp/IP,是因为TCP、IP协议是两个很重要的协议。

这些协议可划分为四层:数据链路层、网络层、传输层、应用层。

应用层:文件传输、电子邮件、文件服务、虚拟终端

  • 超文本传输协议(HTTP):万维网的基本协议
  • 远程登陆(Telnet):提供远程访问其它主机功能,允许用户登录internet主机,并在这台主机上执行命令
  • 文件传输:TFTP(简单文件传输协议)
  • 网络管理:SNMP(简单网络管理协议),提供监控网络设备的方法
  • 域名系统:(DNS),用于在internet中将域名及其公共广播的网络节点转换成IP地址

网络层:为数据包选择路由

  • Internet协议:IP协议
  • Internet控制信息协议(ICMP)
  • 地址解析协议(ARP)
  • 反向地址解析协议(RARP)

数据链路层:传输有地址的帧以及错位检测功能

传输层:提供端对端的接口(TCP、UDP)

关于TCP与UDP的区别,最重要的一点就是:tcp需要三次握手,而udp是面向无连接的,就是指定ip和端口号,可以无限制的发送数据包

二、UDP协议的通信流程

1.创建套接字:

UDP协议是一种不可靠的网络协议,在通信的两端各建立一个socket套接字,这两个socket只是发送、接收数据的对象,

扫描二维码关注公众号,回复: 12461009 查看本文章

Java提供了DatagramSocket类,作为基于UDP协议的Socket

2.创建数据,并把数据打包

Java中提供了DatagramPacket类,作为UDP通信中的数据包,所有的数据都可以构建成一个DatagramPacket

3.发送、接收数据

DatagramSocket类中,提供了send()和receive()方法,用来发送和接收数据,不过要注意创建缓冲区,使用byte[]即可

4.关闭

完成UDP通信,要记得关闭套接字DatagramSocket,释放资源

三、UDP广播的分类

1.广播与单播:

广播UDP与单播UDP的区别就是IP地址不同,广播使用广播地址255.255.255.255,将消息发送到同一广播网络上的每个主机。

注意:本地广播信息是不会被路由器转发。

2.多播:

多播,也称为“组播”,将网络中同一业务类型主句进行了逻辑上的分组,进行数据收发的时候,其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。

在广域网上广播的时候,其中的交换机和路由器只向需要获取数据的主机复制并转发数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择地复制并传输数据,将数据仅仅传输给组内的主机。多播的这种功能,可以一次将数据发送到多个主机,又能保证不影响其他不需要(未加入组)的主机的其他通信。

3.广域网的多播

多播的地址特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类。

1、局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。

2、预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。

3、管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

四、Android实现基于UDP协议的通信

1.发送端

创建套接字

DatagramSocket ds = new DatagramSocket();

创建一个缓冲区

byte[] requestDataBytes = data;//data为数据包中数据

创建数据包

DatagramPacket requestPacket = new DatagramPacket(requestDataBytes,
                requestDataBytes.length);

指定IP和端口号,“255.255.255.255”为广播,单播的话指定对应的ip即可

requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
requestPacket.setPort(30000);

发送数据

ds.send(requestPacket);

关闭套接字

ds.close();

2.接收端

创建套接字,需要指定监听的端口号

DatagramSocket ds = new DatagramSocket(30000);

创建缓冲区,缓冲区大小根据需要指定

final byte[] buf = new byte[512];

创建数据包

DatagramPacket receivePack = new DatagramPacket(buf, buf.length);

开始接收

ds.receive(receivePack);

从接收的数据包中可以直接获取发送端的ip、端口号及数据内容

String ip = receivePack.getAddress().getHostAddress();//发送者的ip
int port = receivePack.getPort();//发送者的端口
int dataLen = receivePack.getLength();//数据包长度
String data = new String(receivePack.getData(), 0, dataLen);//数据内容

关闭套接字

ds.close();

完整代码如下:使用时,应先调用start()创建监听线程,然后根据需要发送数据

public static void sendBroadcast(byte[] data) throws IOException {
        // 作为搜索方,无需指定端口,让系统自动分配
        DatagramSocket ds = new DatagramSocket();

        //发送一份请求数据,暴露监听端口
        byte[] requestDataBytes = data;
        //创建一个DatagramPacket
        DatagramPacket requestPacket = new DatagramPacket(requestDataBytes,
                requestDataBytes.length);
        //指定接收方的ip地址
        requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));

        //指定接收方的端口号
        requestPacket.setPort(30000);
        //开始发送广播
        ds.send(requestPacket);
        ds.close();
    }

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) {
            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[10000];
                    DatagramPacket receivePack = new DatagramPacket(buf, buf.length);

                    //开始接收
                    ds.receive(receivePack);

                    //打印接收到的信息与发送者的信息
                    String ip = receivePack.getAddress().getHostAddress();//发送者的ip
                    int port = receivePack.getPort();//发送者的端口
                    int dataLen = receivePack.getLength();//数据包长度
                    String data = new String(receivePack.getData(), 0, dataLen);//数据内容
                    byte[] audio = new byte[data.length()];
                    System.arraycopy(receivePack.getData(), 0, audio, 0, data.length());
                    AudioModule.getInstance().udpadd(audio);

                }
            } catch (Exception e) {

            } finally {
                close();
            }

        }

        private void close() {
            if (null != ds) {
                ds.close();
                ds = null;
            }
        }

private static Listener listen() throws InterruptedException {
        Log.d(TAG,"UDPSearcher  start listen");
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Listener listener = new Listener(LISTENER_PORT, countDownLatch);
        listener.start();
        countDownLatch.await(); //等待监听启动完成,这里是阻塞的

        return listener;//启动完成就返回监听
    }

public static void start() {
        try {
            Listener listener = listen();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_39837804/article/details/112549932