欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-10 23:40:04
...

一、什么是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只是发送、接收数据的对象,

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();
        }
    }

 

相关标签: android