NIO系列番外——UDP相关

  我本以为NIO已经差不多了,可谁想到… 今天在看upd的时候,惊人的发现了这个东西:DatagramChannel。嗯? channel?NIO? 对,没错。还真的有一个udp用的channel! 我本身对这个东西是心有疑惑的,udp他本身就是一个异步性很强的东西,为什么还需要NIO呢? 我自己暂时想不出个所以然来,但是本着一家人就要阵阵齐齐的心态,还是先整理为好,万一哪天用上了呢?

DatagramChannel

  首先,他是一个SelectableChannel,这也就意味着他可以和selector一起使用。具体和selector结合使用就不多说了,因为和其他的channel用起来差不多。

打开

  也是和其他的Channel很像,没有共有的构造函数,要使用静态方法来获得实例:

1
DatagramChannel channel = DatagramChannel.open();

  接下来的操作也是十分熟悉:获得与channel关联的socket,将其与一个socketAddress绑定。同时和其他channel一样,在java 7之后channel可以直接绑定address。

1
2
3
4
5
6
DatagramSocket socket = channel.socket();
InetSocketAddress address = new InetSocketAddress(port);
socket.bind(address);

//java7之后可以这样写
channel.bind(address);

接收数据

  这里开始就和原来的channel有点不一样了,但是感觉又像DatagramSocket。可以使用receive()方法来接收数据,同时receive方法返回一个SocketAddress来表示数据发送方的地址。因为是channel,理所当然的应该使用bytebuffer来作为数据的载体。

1
2
3
4
public SocketAddress receive(Bytebuffer dst) throws IOException

Bytebuffer buffer = Bytebuffer.allocate(SIZE);
SocketAddress remoteAddress = channel.receive(buffer);

  如果channel是阻塞的(默认状态),这个方法在获取到数据包之前不会返回;如果channel是非阻塞的,没有包读取的情况下,会直接返回null。

发送数据

  send方法可以将一个数据包从bytebuffer 写入 channel:

1
public int send(Bytebuffer src, SocketAddress target) throws IOException

  这个方法返回写入的字节数,第一个方法传入buffer,第二个方法传入目标地址。不过要记住,如果需要将buffer多次发送给不同的目标,不要忘记将buffer“倒带” rewind()

连接

   这个连接就有一个很大的误区了。。。并非字面意思上的连接,而是和DatagramSocket的那个connect差不多。只是名字叫连接,并没有实际的建立连接,连接之后,channel只接受与之连接的地址 发送的数据包 同时也只像那个地址发送数据包。同时还有2个方法来检测channel是否连接/控制channel“断开”连接。但是他其实并不会断开连接,因为connect方法本身就没有建立连接…使用了这个方法后,channel会被允许再次向任何地址 接受/发送数据包

1
2
3
4
5
InetSocketAddress remoteAddress = new InetSocketAddress(remoteHostName,port);
channel.connect(remoteAddress);

public boolean isConnected()
public DatagramChannel disconnect() throws IOException

  其实乍一看这个方法并没有什么用,我也真的是这样认为的..不过在后面就会发现2个熟悉的东西,并且只有在connect之后才能只用。

read/write

  说到channel怎么能少了这两个方法呢??这两位才是读写操作的先祖啊。 之前说的receive 以及 send 是用于特殊用途的读写方法,所以channel当然可以用这些基本的方法,但是有一个条件,那就是上面提到的connect。read 和 write 方法仅能在已 connect的channel上使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract int read(ByteBuffer dst)
throws IOException

public final long read(ByteBuffer[] dsts)
throws IOException

public abstract long read(ByteBuffer[] dsts,int offset,int length)
throws IOException



public abstract int write(ByteBuffer src)
throws IOException

public final long write(ByteBuffer[] srcs)
throws IOException

public abstract long write(ByteBuffer[] srcs,int offset,int length)
throws IOException

关闭

  有开就有关,总体来说差不多,可以通过调用close来关闭channel,但是还是用try-with-recources比较好

1
public void close() throws IOException