Android DatagramChannel 使用

作者: 寒江楓雨 | 来源:发表于2019-04-18 17:08 被阅读2次

    一 概述
    Java NIO中的DatagramChannel定义在java.nio.channels包中,是一个能收发UDP包的通道。 因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
    SocketChannel对应Socket,ServerSocketChannel对应ServerSocket,每一个DatagramChannel对象也有一个关联的DatagramSocket对象。
    不过原命名模式在此并未适用:“DatagramSocketChannel”显得有点笨拙,因此采用了简洁的“DatagramChannel”名称。正如SocketChannel模拟连接导向的流协议(如TCP/IP),DatagramChannel则模拟包导向的无连接协议(如UDP/IP)。

    二 DatagramChannel创建

    如下是打开DatagramChannel的方式:

    DatagramChannel channel = DatagramChannel.open(); // 获取通道
    channel.socket().bind(new InetSocketAddress(8080)); // 绑定端口
    

    这个例子打开的DatagramChannel可以在UDP端口8080上接收数据包。

    三 接收数据

    通过receive()方法从DatagramChannel接收数据,如:

    ByteBuffer buffer = ByteBuffer.allocate(48); // 分配Buffer
    buffer.clear(); // 清空Buffer
    SocketAddress socketAddress = datagramChannel.receive(buffer); // 接受客户端发送数据
    

    receive()方法会将接收到的数据包内容复制到指定的Buffer。如果Buffer容不下收到的数据,多出的数据将被丢弃。

    四 发送数据

    通过send()方法从DatagramChannel发送数据,如:

    DatagramChannel channel = DatagramChannel.open(); // 获取通道
    String data= "发送数据测试"; // 将要发送的数据
    ByteBuffer buffer = ByteBuffer.allocate(48); // 缓冲区分配
    buffer.clear(); // 清空缓冲区
    buffer.put(data.getBytes("UTF-8")); // 将数据写入缓冲区
    buffer.flip(); // 切换数据模式
    int bytesSent = channel.send(buffer, new InetSocketAddress(ip, 8080)); // 发送数据到ip服务,80端口(port)
    

    这个例子发送一串字符到ip服务器的UDP端口8080。因为服务端并没有监控这个端口,所以什么也不会发生。

    也不会通知你发出的数据包是否已收到,因为UDP在数据传送方面没有任何保证。

    五 连接到特定的地址

    可以将DatagramChannel"连接"到网络中的特定地址的。由于UDP是无连接的,连接到特定地址并不会像TCP通道那样

    创建一个真正的连接。而是锁住DatagramChannel,让其只能从特定地址收发数据。

    当连接后,也可以使用read()和write()方法,就像在用传统的通道一样。只是在数据传送方面没有任何保证。示例如下:

    channel.connect(new InetSocketAddress(ip, 8080));
    int bytesRead = channel.read(buffer);
    int bytesWritten = channel.write(buffer);
    

    六 下面是一个完整例子

    package com.zongmu.rpa.probes;
    
    import android.os.StrictMode;
    import android.text.TextUtils;
    
    import com.google.protobuf.InvalidProtocolBufferException;
    import com.google.protobuf.Message;
    import com.google.protobuf.RpcCallback;
    import com.zongmu.rpa.constant.Constants;
    import com.zongmu.rpa.model.ProtoPackage;
    import com.zongmu.rpa.utils.ByteUtils;
    import com.zongmu.rpa.utils.LogUtil;
    import com.zongmu.rpa.utils.NetworkUtil;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.net.SocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.DatagramChannel;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.util.Iterator;
    
    public class UdpProbes implements ZmProbes {
        private static String TAG = "UdpProbes";
        private Selector mSelector;
        private DatagramChannel mDatagramChannel;
        private ProtoPackage mProtoPackage;
        private String mRemoteIp = "";
        @Override
        public void start() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        mSelector = Selector.open();
                        mDatagramChannel = DatagramChannel.open();
                        mDatagramChannel.bind(new InetSocketAddress(Constants.UDP_PORT));
                        mDatagramChannel.configureBlocking( false ) ;
                        //注册
                        mDatagramChannel.register( mSelector , SelectionKey.OP_READ ) ;
                        LogUtil.e(TAG, "udp socket connect success");
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtil.e(TAG, "udp socket connect failed"+e.toString());
                    }
                }
            }).start();
        }
    
        public String getmRemoteIp() {
            return mRemoteIp;
        }
    
        @Override
        public void sendData(final byte[] pkg) {
            LogUtil.d(TAG,"Send UDP Package");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
                    StrictMode.setThreadPolicy(policy);
                    try {
                        DatagramChannel mDatagramChannel = DatagramChannel.open();
                        mDatagramChannel.socket().setBroadcast(true);
                        ByteBuffer buffer = ByteBuffer.allocate(512);
                        buffer = ByteBuffer.wrap(pkg);
                        buffer.flip();
                        mDatagramChannel.send(ByteBuffer.wrap(pkg), new InetSocketAddress(NetworkUtil.getBroadcastAddress(),      Constants.BROADCAST_PORT));
                        LogUtil.e(TAG, "Broadcast packet sent to: " + NetworkUtil.getBroadcastAddress());
                    } catch (IOException e) {
                        LogUtil.e(TAG, "IOException: " + e.getMessage());
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    
        @Override
        public int receData(Message responsePrototype, RpcCallback<Message> done) {
            if (mDatagramChannel == null){
                return Constants.SOCKET_NOT_CONNECT;
            }
            int ret = -1;
            try {
                ret = mSelector.select(2000);
            } catch (IOException e) {
                e.printStackTrace();
            }
            LogUtil.d(TAG,"Selector ret = "+ret);
            if(ret < 0){
                return Constants.RECEIVE_DATA_TIMEOUT;
            }
            
            byte[] buffer = new byte[512];
            Iterator<SelectionKey> iterator = mSelector.selectedKeys().iterator();
            while(iterator.hasNext()){
                SelectionKey key = iterator.next();
                if(key.isValid()) {
                    if(key.isReadable()) {
                        LogUtil.e(TAG, "receive data");
                        try {
                            SocketAddress socketAddress = mDatagramChannel.receive(ByteBuffer.wrap(buffer));
                            String[]  addr = socketAddress.toString().split(":");
                            mRemoteIp = addr[0].substring(1);
                            LogUtil.e(TAG, "receive remote " + socketAddress.toString());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }else {
                    LogUtil.e(TAG, "wait server response timeout");
                    buffer = null;
                }
                if (!iterator.hasNext()) {
                    break;
                }
            }
    
            if(mProtoPackage == null){
                mProtoPackage = new ProtoPackage();
            }
            
            String data = new String(buffer, 0, 13);
            LogUtil.e(TAG,"data:"+ByteUtils.bytes2HexString(ByteUtils.getSubArrays(buffer,0,40)));
            if (TextUtils.equals("ZongMuService", data)) {
                LogUtil.e(TAG, "receive server response");
                mProtoPackage.setPackageBuffer(buffer);
                mProtoPackage.parsePackage();
                byte[] serviceParamPackage = mProtoPackage.getParamPackage();
                try {
                    responsePrototype = responsePrototype.getParserForType().parseFrom(serviceParamPackage);
                    done.run(responsePrototype);
                } catch (InvalidProtocolBufferException e) {
                    e.printStackTrace();
                }
                buffer = null;
                LogUtil.d(TAG,"callback finish");
            }else {
                buffer = null;
                return Constants.RECEIVE_DATA_ERROR;
            }
            return Constants.RECEIVE_DATA_SUCCESS;
        }
    
        @Override
        public void stop() {
            try {
                if (mDatagramChannel != null) {
                    mSelector.close();
                    mDatagramChannel.close();
                    mDatagramChannel = null;
                    LogUtil.e( TAG,"udp socket closed");
                }
            } catch (IOException e) {
                e.printStackTrace();
                LogUtil.e( TAG,"udp socket closed:"+e.toString());
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Android DatagramChannel 使用

        本文链接:https://www.haomeiwen.com/subject/nebtgqtx.html