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