您的位置:首页 > 编程语言 > Java开发

Java NIO基本使用

2016-02-17 22:58 423 查看
NIO是Java提供的非阻塞I/OAPI.

非阻塞的意义在于可以使用一个线程对大量的数据连接进行处理,非常适用于"短数据长连接"的应用场景,例如即时通讯软件.

在一个阻塞C/S系统中,服务器要为每一个客户连接开启一个线程阻塞等待客户端发送的消息.若使用非阻塞技术,服务器可以使用一个线程对连接进行轮询,无须阻塞等待.这大大减少了内存资源的浪费,也避免了服务器在客户线程中不断切换带来的CPU消耗,服务器对CPU的有效使用率大大提高.



其核心概念包括Channel,Selector,SelectionKey,Buffer.

Channel是I/O通道,可以向其注册Selector,应用成功可以通过select操作获取当前通道已经准备好的可以无阻塞执行的操作.这由SelectionKey表示.

SelectionKey的常量字段SelectionKey.OP_***分别对应Channel的几种操作例如connect(),accept(),read(),write().



select操作后得到SelectionKey.OP_WRITE或者READ即可在Channel上面无阻塞调用read和write方法,Channel的读写操作均需要通过Buffer进行.即读是讲数据从通道中读入Buffer然后做进一步处理.写需要先将数据写入Buffer然后通道接收Buffer.



下面是一个使用NIO的基本C/S示例.该示例只为显示如何使用基本的API而存在,其代码的健壮性,合理性都不具参考价值.

这个示例,实现一个简单的C/S,客户端想服务器端发送消息,服务器将收到的消息打印到控制台.现实的应用中需要定义发送数据使用的协议,以帮助服务器解析消息.本示例只是无差别的使用默认编码将收到的字节转换字符并打印.通过改变初始分配的ByteBuffer的容量,可以看到打印消息的变化.容量越小,对一条消息的处理次数就越多,容量大就可以在更少的循环次数内读完整个消息.所以真是的应用场景,要考虑适当的缓存大小以提高效率.

首先是Server

01
package
hadix.demo.nio;
02
03
import
java.io.IOException;
04
import
java.net.InetSocketAddress;
05
import
java.nio.ByteBuffer;
06
import
java.nio.channels.SelectionKey;
07
import
java.nio.channels.Selector;
08
import
java.nio.channels.ServerSocketChannel;
09
import
java.nio.channels.SocketChannel;
10
import
java.util.*;
11
import
java.util.concurrent.ConcurrentHashMap;
12
13
/**
14
*
User:hAdIx
15
*
Date:11-11-2
16
*
Time:上午11:26
17
*/
18
public
class
Server
{
19
private
Selector
selector;
20
private
ByteBuffer
readBuffer=ByteBuffer.allocate(
8
);
//调整缓存的大小可以看到打印输出的变化
21
private
Map<SocketChannel,
byte
[]>
clientMessage=
new
ConcurrentHashMap<>();
22
23
public
void
start()
throws
IOException
{
24
ServerSocketChannel
ssc=ServerSocketChannel.open();
25
ssc.configureBlocking(
false
);
26
ssc.bind(
new
InetSocketAddress(
"localhost"
,
8001
));
27
selector
=Selector.open();
28
ssc.register(selector,SelectionKey.OP_ACCEPT);
29
while
(!Thread.currentThread().isInterrupted())
{
30
selector.select();
31
Set<SelectionKey>
keys=selector.selectedKeys();
32
Iterator<SelectionKey>
keyIterator=keys.iterator();
33
while
(keyIterator.hasNext())
{
34
SelectionKey
key=keyIterator.next();
35
if
(!key.isValid())
{
36
continue
;
37
}
38
if
(key.isAcceptable())
{
39
accept(key);
40
}
else
if
(key.isReadable())
{
41
read(key);
42
}
43
keyIterator.remove();
44
}
45
}
46
}
47
48
private
void
read(SelectionKey
key)
throws
IOException
{
49
SocketChannel
socketChannel=(SocketChannel)key.channel();
50
51
//
Clearoutourreadbuffersoit'sreadyfornewdata
52
this
.readBuffer.clear();
53
54
//
Attempttoreadoffthechannel
55
int
numRead;
56
try
{
57
numRead
=socketChannel.read(
this
.readBuffer);
58
}
catch
(IOException
e){
59
//
Theremoteforciblyclosedtheconnection,cancel
60
//
theselectionkeyandclosethechannel.
61
key.cancel();
62
socketChannel.close();
63
clientMessage.remove(socketChannel);
64
return
;
65
}
66
67
byte
[]
bytes=clientMessage.get(socketChannel);
68
if
(bytes
==
null
)
{
69
bytes
=
new
byte
[
0
];
70
}
71
if
(numRead
>
0
)
{
72
byte
[]
newBytes=
new
byte
[bytes.length
+numRead];
73
System.arraycopy(bytes,
0
,newBytes,
0
,bytes.length);
74
System.arraycopy(readBuffer.array(),
0
,newBytes,bytes.length,numRead);
75
clientMessage.put(socketChannel,newBytes);
76
System.out.println(
new
String(newBytes));
77
}
else
{
78
String
message=
new
String(bytes);
79
System.out.println(message);
80
}
81
}
82
83
private
void
accept(SelectionKey
key)
throws
IOException
{
84
ServerSocketChannel
ssc=(ServerSocketChannel)key.channel();
85
SocketChannel
clientChannel=ssc.accept();
86
clientChannel.configureBlocking(
false
);
87
clientChannel.register(selector,SelectionKey.OP_READ);
88
System.out.println(
"a
newclientconnected"
);
89
}
90
91
92
public
static
void
main(String[]
args)
throws
IOException
{
93
System.out.println(
"server
started..."
);
94
new
Server().start();
95
}
96
}
然后是Client

01
package
hadix.demo.nio;
02
03
import
java.io.IOException;
04
import
java.net.InetSocketAddress;
05
import
java.nio.ByteBuffer;
06
import
java.nio.channels.SelectionKey;
07
import
java.nio.channels.Selector;
08
import
java.nio.channels.SocketChannel;
09
import
java.util.Iterator;
10
import
java.util.Scanner;
11
import
java.util.Set;
12
13
/**
14
*
User:hAdIx
15
*
Date:11-11-2
16
*
Time:上午11:26
17
*/
18
public
class
Client
{
19
20
public
void
start()
throws
IOException
{
21
SocketChannel
sc=SocketChannel.open();
22
sc.configureBlocking(
false
);
23
sc.connect(
new
InetSocketAddress(
"localhost"
,
8001
));
24
Selector
selector=Selector.open();
25
sc.register(selector,SelectionKey.OP_CONNECT);
26
Scanner
scanner=
new
Scanner(System.in);
27
while
(
true
)
{
28
selector.select();
29
Set<SelectionKey>
keys=selector.selectedKeys();
30
System.out.println(
"keys="
+
keys.size());
31
Iterator<SelectionKey>
keyIterator=keys.iterator();
32
while
(keyIterator.hasNext())
{
33
SelectionKey
key=keyIterator.next();
34
keyIterator.remove();
35
if
(key.isConnectable())
{
36
sc.finishConnect();
37
sc.register(selector,SelectionKey.OP_WRITE);
38
System.out.println(
"server
connected..."
);
39
break
;
40
}
else
if
(key.isWritable())
{
41
42
System.out.println(
"please
inputmessage"
);
43
String
message=scanner.nextLine();
44
ByteBuffer
writeBuffer=ByteBuffer.wrap(message.getBytes());
45
sc.write(writeBuffer);
46
}
47
}
48
}
49
}
50
51
public
static
void
main(String[]
args)
throws
IOException
{
52
new
Client().start();
53
}
54
}
此外有一个代码写得更好的例子,非常值得参考.http://rox-xmlrpc.sourceforge.net/niotut/index.html

这个例子里面的客户端将消息发送给服务器,服务器收到后立即写回给客户端.例子中代码虽然也没有做有意义的处理,但是其结构比较合理,值得以此为基础进行现实应用的扩展开发.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: