java---非阻塞数据报

原创 2013年04月10日 22:24:55

非阻塞数据报.doc数据报(UDP)信道

Java的NIO包通过DatagramChannel类实现了数据报(UDP)信道。与我们之前看到的其他形式的SelectableChannel一样,DatagramChannel在DatagramSocket上添加了选择和非阻塞行为,以及基于缓冲区的I/O操作能力。

DatagramChannel: 创建,连接和关闭

static DatagramChannel open()

boolean isOpen()

DatagramSocket socket() void close()

需要调用DatagramChannel的open()工厂方法来创建一个DatagramChannel实例,该实例是未绑定的。DatagramChannel只是对基本DatagramSocket的一个包装器(wrapper)。使用其socket()方法可以直接访问内部的DatagramSocket实例。这就允许通过调用基本的DatagramSocket方法进行绑定、设置套接字选项等操作。用完DatagramChannel后,要调用它的close()方法将其关闭。

只要创建了一个DatagramChannel实例,就可以非常直接地发送和接收数据。

DatagramChannel: 发送和接收

int send(ByteBuffer src, SocketAddress target)
SocketAddress receive(ByteBuffer dst)

send()方法用于创建一个包含了给定ByteBuffer中的数据的数据报文,并将其发送到目的地址指定的SocketAddress上。receive()方法用于将接收到的数据报文存入指定缓冲区并返回发送者的地址。重要提示:如果缓冲区的剩余空间小于数据报文中的数据大小,多余的数据将毫无提示地丢弃。

以下代码段用于创建一个DatagramChannel实例,并将UTF-16编码的字符串"Hello"发送到运行在同一主机的5000端口上的UDP服务器上。

DatagramChannel channel = DatagramChannel.open();
ByteBuffer buffer = ByteBuffer.wrap("Hello".getBytes("UTF-16"));
channel.send(buffer, new InetSocketAddress("localhost", 5000));

以下代码段用于创建一个DatagramChannel实例,将底层的套接字绑定到5000端口,接收最长为20字节的数据报文,并将字节转换成使用UTF-16编码的字符串。

DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(5000));
ByteBuffer buffer = ByteBuffer.allocateDirect(20);
SocketAddress address = channel.receive(buffer);
buffer.flip();
String received = Charset.forName("UTF-16").
newDecoder().decode(buffer).toString();

在上面的send()实例中,调用send()方法时并没有显式地绑定本地端口,因此将随机选择一个可用端口。相应的receive()方法用于返回一个SocketAddress,其中包含了端口号。

如果总是向同一个远程终端发送或接收数据,我们可以选择调用connect()方法,并使用 SocketAddress指定远程终端的地址。

DatagramChannel: 连接DatagramChannel

DatagramChannel connect(SocketAddress remote)
DatagramChannel disconnect()
boolean isConnected()
int read(ByteBuffer dst)
long read(ByteBuffer[] dsts)
long read(ByteBuffer[] dsts, int offset, int length)
int write(ByteBuffer src)
long write(ByteBuffer[] srcs)
long write(ByteBuffer[] srcs, int offset, int length)

这些方法限制我们只能通过指定的地址发送和接收数据。为什么要这样做呢?原因之一是调用connect()方法后,可以使用read()和write()方法来代替receive()和send()方法,并且不需要处理远程地址。read()和write()方法分别用于接收和发送一个数据报文。分散式读操作以一个ByteBuffer数组为参数,只接收一个数据报文,并按顺序将其填入缓冲区中。聚集式写操作将缓冲区数组中的所有字节连接起来创建一个要传输的数据报文。重要提示:现在能够发送的最大数据报文可以包含65507个字节,试图发送更多的数据将被无提示地截断。

使用connect()方法的另一个好处是,已建立连接的数据报文信道可能只接收从指定终端发送来的数据,因此我们不需要测试接收端的有效性。注意,DatagramChannel的connect()方法只起到限制发送和接收终端的作用,连接时并没有数据包在SocketChannel上进行交换,而且也不需要像SocketChannel那样等待或测试连接是否完成。(见第6章)

到目前为止DatagramChannel看起来与DatagramSocket非常相似。数据报文信道和套接字的主要区别是,信道可以进行非阻塞I/O操作和使用选择器。DatagramChannel中选择器的创建,信道的注册、选择等,与SocketChannel几乎一模一样。有一个区别是DatagramChannel不能注册连接I/O操作,不过也不需要这样做,因为DatagramChannel的connect()方法永远不会阻塞。

DatagramChannel: 设置阻塞行为和使用选择器

SelectableChannel configureBlocking(boolean block)
boolean isBlocking()
SelectionKey register(Selector sel, int ops)
SelectionKey register(Selector sel, int ops, Object attachment)
boolean isRegistered()
int validOps()
SelectionKey keyFor(Selector sel)

这些方法的功能与SocketChannel和ServerSocketChannel中的相应方法一样。

下面使用DatagramChannel对第4章中的DatagramSocket UDP回显服务器进行重写。服务器侦听指定的端口,并将接收到的数据报文简单地回发给客户端。重写后的服务器与原来版本的主要区别是它不会在send()和receive()方法上阻塞等待。

UDPEchoServerSelector.java

0 import java.io.IOException;
1 import java.net.InetSocketAddress;
2 import java.net.SocketAddress;
3 import java.nio.ByteBuffer;
4 import java.nio.channels.DatagramChannel;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.Selector;
7 import java.util.Iterator;
8
9 public class UDPEchoServerSelector {
10
11 private static final int TIMEOUT = 3000; // Wait timeout (milliseconds)
12
13 private static final int ECHOMAX = 255; // Maximum size of echo datagram
14
15 public static void main(String[] args) throws IOException {
16
17 if (args.length != 1) // Test for correct argument list
18 throw new IllegalArgumentException("Parameter(s): <Port>");
19
20 int servPort = Integer.parseInt(args[0]);
21
22 // Create a selector to multiplex client connections.
23 Selector selector = Selector.open();
24
25 DatagramChannel channel = DatagramChannel.open();
26 channel.configureBlocking(false);
27 channel.socket().bind(new InetSocketAddress(servPort));
28 channel.register(selector, SelectionKey.OP_READ, new ClientRecord());
29
30 while (true) { // Run forever, receiving and echoing datagrams
31 // Wait for task or until timeout expires
32 if (selector.select(TIMEOUT) == 0) {
33 System.out.print(".");
34 continue;
35 }
36
37 // Get iterator on set of keys with I/O to process
38 Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
39 while (keyIter.hasNext()) {
40 SelectionKey key = keyIter.next(); // Key is bit mask
41
42 // Client socket channel has pending data?
43 if (key.isReadable())
44 handleRead(key);
45
46 // Client socket channel is available for writing and
47 // key is valid (i.e., channel not closed).
48 if (key.isValid() && key.isWritable())
49 handleWrite(key);
50
51 keyIter.remove();
52 }
53 }
54 }
55
56 public static void handleRead(SelectionKey key) throws IOException {
57 DatagramChannel channel = (DatagramChannel) key.channel();
58 ClientRecord clntRec = (ClientRecord) key.attachment();
59 clntRec.buffer.clear(); // Prepare buffer for receiving
60 clntRec.clientAddress = channel.receive(clntRec.buffer);
61 if (clntRec.clientAddress != null) { // Did we receive something?
62 // Register write with the selector
63 key.interestOps(SelectionKey.OP_WRITE);
64 }
65 }
66
67 public static void handleWrite(SelectionKey key) throws IOException {
68 DatagramChannel channel = (DatagramChannel) key.channel();
69 ClientRecord clntRec = (ClientRecord) key.attachment();
70 clntRec.buffer.flip(); // Prepare buffer for sending
71 int bytesSent = channel.send(clntRec.buffer, clntRec.clientAddress);
72 if (bytesSent != 0) { // Buffer completely written?
73 // No longer interested in writes
74 key.interestOps(SelectionKey.OP_READ);
75 }
76 }
77
78 static class ClientRecord {
79 public SocketAddress clientAddress;
80 public ByteBuffer buffer = ByteBuffer.allocate(ECHOMAX);
81 }
82 }

UDPEchoServerSelector.java

非阻塞方式下Socket读取数据的一个例子

 标题:非阻塞方式下Socket读取数据的一个例子作者:DreamTiger发表时间:2000-1-15 下午 05:59:13发信人: lBlade (刀锋), 信区: Delphi 标...
  • ghj1976
  • ghj1976
  • 2001年08月19日 16:17
  • 9295

JAVA基于UDP的多点广播数据报技术的一个实现例子

com.han.Weather This is a UDP network program. The following presents a multicast datagram prog...

java实现UDP广播数据报

要求:        主机不断地重复播出节目预报,可以保证加入到同一组
  • tan313
  • tan313
  • 2014年11月20日 11:09
  • 774

Java数据报Datagram通讯

Java数据报Datagram通讯 8.3.10数据报Datagram通讯   前面在介绍TCP/IP协议的时候,我们已经提到,在TCP/IP协议的传输层除了TCP协议之外还有一个UDP协议,相比而...
  • yihui8
  • yihui8
  • 2012年11月06日 20:00
  • 602

Java 网络编程(六) 使用无连接的数据报(UDP)进行通信

使用无连接的数据报(UDP)进行通信 什么是Datagram?   数据报是网上传输的独立数据包,数据报是否能正确地到达目的地,到达的时间,顺序,内容的正确性均没有保障。     Java中使...

关于Java中数据报套接字DatagramSocket中connet()方法说明

大家都知道UDP协议是面向无连接的,但是用于UDP的DatagramSocket类中却有connet()方法,实在令人费解。刚才有学生问起来,研究了下才发现这个方法的真正用法! connet()方法...

解析IP数据报

  • 2012年11月12日 19:31
  • 6KB
  • 下载

集成Access数据报表功能源码

  • 2009年12月27日 10:43
  • 214KB
  • 下载

java集合+迭代器+数据结构概括+线程同步/异步和线程阻塞/非阻塞

一 集合类概述 为什么出现集合类?面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,Java就提供了集合类。以对象来管理对象。数组和集合类同是容器,有何不同?数组虽然也可以...
  • musk6
  • musk6
  • 2016年12月18日 21:59
  • 330
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java---非阻塞数据报
举报原因:
原因补充:

(最多只允许输入30个字)