Step By Step(Java 网络篇) (八)

2014-11-24 03:03:08 · 作者: · 浏览: 5
);
35 totalBytesRecv += bytesRecv;
36 System.out.print(".");
37 }
38 System.out.println("Received: " + new String(readBuf.array(),0,totalBytesRecv));
39 clntChannel.close();
40 }
41 }
需要说明的是,上面的示例代码只是用最为简单的方式来演示nio中SocketChannel和ByteBuffer等常用对象的使用方法。由于网络的运行状况是比较复杂的,上面的代码在真实的环境中并不适合,因为有很多异常的场景没有被考虑进来,而这些异常的处理方式尽管确实存在一些通用的技巧,但是在真实环境中仍然需要结合不同的应用方式作出必要的调整和优化。而该部分内容的讨论是需要另外一个专门的议题并结合实际的案例来予以讲述的。希望今后有机会和大家来共同探讨。
Selector类可用于避免使用非阻塞式客户端中很浪费资源的“忙等”方法。例如,考虑一个即时消息服务器。可能有上千个客户端同时连接到服务器,但在任何时刻只有非常少量的消息需要读取和分发。这就需要一种方法阻塞等待,直到至少有一个信道可以进行I/O操作,并指出那个信道。NIO的Selector就实现了这样的功能。一个Selector实例可以同时检查一组信道的I/O状态。用专业术语说,Selector就一个多路开关选择器,因为一个Selector能够管理多个信道上的I/O操作。
1 interface TCPProtocal {
2 //当有新客户端连接到服务器时,调用该函数
3 void handleAccept(SelectionKey key) throws IOException;
4 //当客户端有新的数据发送到服务器时,调用该函数
5 void handleRead(SelectionKey key) throws IOException;
6 //当可以写数据到客户端时条用该函数。
7 void handleWrite(SelectionKey key) throws IOException;
8 }
9
10 class SelectorProtocal implements TCPProtocal {
11 private int bufSize;
12 public SelectorProtocal(int bufSize) {
13 this.bufSize = bufSize;
14 }
15 @Override
16 public void handleAccept(SelectionKey key) throws IOException {
17 //1. 通过发生的事件(key)来获取发生事件的通道,一般而言,发生Accept事件的Channel
18 //通常为Server端负责监听的SocketChannel。之后再通过该通道接收来自客户端的连接请求,
19 //最后返回用于之后I/O操作的客户端信道。
20 SocketChannel clntChannel = ((ServerSocketChannel)key.channel()).accept();
21 //2. 同样将该客户端的SocketChannel设置为非阻塞模式。
22 clntChannel.configureBlocking(false);
23 //3. 将Accept返回的客户端SocketChannel也注册到产生当前事件的Selector实例中,
24 //同时告知Selector,只是感知该SocketChannel的READ事件,即有数据到来是触发。
25 //第三个参数是附件参数,这里可以放置任何对象和该SocketChannel关联,在该事件
26 //发生时,可以通过key的attachment方法获取该附件对象。
27 clntChannel.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocate(bufSize));
28 }
29 @Override
30 public void handleRead(SelectionKey key) throws IOException {
31 //1. 同样通过发生的事件(key)获取产生事件的通道。
32 SocketChannel clntChannel = (SocketChannel)key.channel();
33 //2. 再通过key获取存储数据的ByteBuffer对象。
34 ByteBuffer buf = (ByteBuffer)key.attachment();
35 long bytesRead = clntChannel.read(buf);
36 if (bytesRead == -1) {
37 clntChannel.close();
38 } else if (bytesRead > 0) {
39 //这里将重新设置该key在Selector中感兴趣的事件。
40 //由于通常在收到数据后可能需要返回应答数据给客户端,因此这里对该
41 //SocketChannel增加了OP_WRITE写事件。
42 key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
43 }
44 }
45 @Override
46 public void handleWrite(SelectionKey key) throws IOException {
47 ByteBuffer buf = (ByteBuffer)key.attachment();
48 buf.flip();
49 SocketChannel clntChannel = (SocketChannel)key.channel();
50 clntChannel.write(buf);
51 //如果所有应答数据都发送,则需要将该So