标签:
在实现新I/O的客户端时,调用静态工厂方法SocketChannel.open()来创建一个新的java.nio.channels.SocketChannel对象。这个方法的参数是一个java.net.SocketAddress对象,指示要连接的主机和端口。
例如:下面的代码段连接指向rama.poly.edu端口19的通道:
SocketAddress rama = new InetSocketAddress("rama.poly.edu“,19);
SocketChannel client = SocketChannel.open(rama);
利用通道,可以直接写入通道本身,而不是写入ByteBuffer对象。
ByteBuffer buffer = ByteBuffer.allocate(74);
将这个ByteBuffer对象传递给通道的read()方法。通道会用从Socket读取的数据填充这个缓冲区。它返回成功读取并存储在缓存区的字节数:
int bytesRead = client.read(buffer);
这会至少读取一个字节,或者返回-1指示数据结束。
示例11-1:一个基于通道的chargen客户端
<span style="font-size:18px;">import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.io.IOException;
public class ChargenClient {
public static int DEFAULT_PORT = 19;
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java ChargenClient host [port]");
return;
}
int port;
try {
port = Integer.parseInt(args[1]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
try {
SocketAddress address = new InetSocketAddress(args[0], port);
SocketChannel client = SocketChannel.open(address);
ByteBuffer buffer = ByteBuffer.allocate(74);
WritableByteChannel out = Channels.newChannel(System.out);
while (client.read(buffer) != -1) {
buffer.flip();
out.write(buffer);
buffer.clear();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}</span>client.configureBlocking(false);
在非阻塞模式下,read()可能因为读不到任何数据而返回0。因此循环需要有些差别:
while(true){
//把每次循环都要允许的代码都放在这里,无论有没有读到数据
int n = client.read(buffer);
if(n > 0) {
buffer.flip();
out.write(buffer);
buffer.clear();
}else if( n == -1) {
//这不应当发生,除非服务器发送故障
break;
}
}
示例11-2:一个非阻塞的chargen服务器
<span style="font-size:18px;">import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
public class ChargenServer {
public static int DEFAULT_PORT = 19;
public static void main(String[] args) {
int port;
try {
port = Integer.parseInt(args[0]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
System.out.println("Listening for connections on port " + port);
byte[] rotation = new byte[95*2];
for (byte i = ' '; i <= '~'; i++) {
rotation[i -' '] = i;
rotation[i + 95 - ' '] = i;
}
ServerSocketChannel serverChannel;
Selector selector;
try {
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException ex) {
ex.printStackTrace();
return;
}
while (true) {
try {
selector.select();
} catch (IOException ex) {
ex.printStackTrace();
break;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey key2 = client.register(selector, SelectionKey.
OP_WRITE);
ByteBuffer buffer = ByteBuffer.allocate(74);
buffer.put(rotation, 0, 72);
buffer.put((byte) '\r');
buffer.put((byte) '\n');
buffer.flip();
key2.attach(buffer);
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
if (!buffer.hasRemaining()) {
// Refill the buffer with the next line
buffer.rewind();
// Get the old first character
int first = buffer.get();
// Get ready to change the data in the buffer
buffer.rewind();
// Find the new first characters position in rotation
int position = first - ' ' + 1;
// copy the data from rotation into the buffer
buffer.put(rotation, position, 72);
// Store a line break at the end of the buffer
buffer.put((byte) '\r');
buffer.put((byte) '\n');
// Prepare the buffer for writing
buffer.flip();
}
client.write(buffer);
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
}
catch (IOException cex) {}
}
}
}
}
}</span>
流和通道之间的关键区别在于流是基于字节的,而通道是基于块的。
无论缓冲区是何种类型,都有相同的方法来获取和设置缓冲区4个关键部分地信息。
位置
缓存区中将读取或写入的下一个位置。
public final int position()
public final Buffer position(int newPosition)
容量
缓冲区可以保存的元素的最大数目。容量值在创建缓冲区时设置,此后不能改变。
public final int capacity()
限度
缓冲区中可访问数据的末尾位置。只要不改变限度,就无法读/写超过这个位置的数据,即使缓冲区有更大的容量也没有用。
public final int limit()
public final Buffer limit(int newLimit)
标记
缓冲区中客户端指定的索引。通过调用mark()可以将标记设置为当前位置。调用reset()可以将当前位置设置为所标记的位置。
公共的Buffer超类提供了另外几个方法:
public final Buffer clear()
clear()方法将位置设置为0,并将限度设置为容量,从而将缓冲区“清空”。
public final Buffer rewind()
将位置设置为0,但不改变限度,还允许重新读取缓冲区
public final Buffer flip()
将限度设置为当前位置,位置设置为0,希望排空刚刚填充的缓冲区时可以调用这个方法。
public final int remaining()
返回缓冲区中当前位置与限度之间的元素数。
public final boolean hasRemaining()_
如果剩余元素大于0,hasRemaining()方法返回true
标签:
原文地址:http://blog.csdn.net/u010819416/article/details/51252622