Android DatagramChannel 使用

一 概述
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 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 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 使用)