阻塞式与非阻塞式(主要针对网络编程而言)
阻塞式:
比如到你某个时候到A楼一层(假如是内核缓冲区)取快递,但是你不知道快递什么时候过来,你又不能干别的事,只能死等着。非阻塞式:
还是等快递的例子:你知道今天要来快递,等快递员给你打电话,在快递员打电话之前你可以忙自己的事情,等接到电话后再过去取,这就是非阻塞式的。
为进一步提高IO的效率,使用了多线程来解决这个问题,但是线程的数量总是会因为CPU的切换到达负荷值。
选择器是用来监控通道的I/O的状态,等Client的数据准备就绪,就通知Server来处理,这期间Server中的线程可以做其他的事情。
使用NIO完成网络通信的三个核心
1)连接Channel
2)缓冲区Buffer
3)选择器Select:用于监控SelectbleChannel的IO状态。
阻塞式网络通信实现
@Test
public void Server() throws IOException {
// 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
FileChannel fon = FileChannel.open(Paths.get("doc2"), StandardOpenOption.WRITE);
// 绑定端口号
ssChannel.bind(new InetSocketAddress(9989));
// 接受来自客户端的请求
SocketChannel sChannel = ssChannel.accept();
// 建立缓冲区用于传输数据
ByteBuffer buf = ByteBuffer.allocate(1024);
while (sChannel.read(buf) != -1) {
buf.flip();
fon.write(buf);
buf.clear();
}
String str = new String("传输完成");
buf.put(str.getBytes());
buf.flip();
sChannel.write(buf);
sChannel.shutdownOutput();
ssChannel.close();
fon.close();
sChannel.close();
}
@Test
public void Client() throws IOException {
// 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9989));
// 关联文件,将文件数据发送到客户端,并且保存接收来自客户端的数据
FileChannel fin = FileChannel.open(Paths.get("doc"), StandardOpenOption.READ, StandardOpenOption.WRITE);
// 利用缓冲区保存数据
ByteBuffer buf = ByteBuffer.allocate(1024);
// 利用通道传输数据
while (fin.read(buf) != -1) {
buf.flip();
sChannel.write(buf);
buf.clear();
}
int length = 0;
while ((length = sChannel.read(buf)) != -1) {
buf.flip();
System.out.println(new String(buf.array(), 0, length));
buf.clear();
}
sChannel.shutdownOutput();
sChannel.close();
fin.close();
}
非阻塞式实现
@Test
public void Client() throws IOException {
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9988));
// 切换为非阻塞式的
sChannel.configureBlocking(false);
// 申请缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
// 写入日期
buf.put(new Date().toString().getBytes());
buf.flip();
sChannel.write(buf);
// 关闭缓冲区
sChannel.close();
}
@Test
public void Server() throws IOException {
ServerSocketChannel ssChannel = ServerSocketChannel.open();
// 绑定端口号
ssChannel.bind(new InetSocketAddress(9988));
// 切换为非阻塞式
ssChannel.configureBlocking(false);
// 构造选择器
Selector selector = Selector.open();
// 将通道注册到相应的选择器上(后面的参数表示监控的通道的状态:包含读、写、连接、接收)
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
// 通过选择器轮回的方式获取选择器上已经准备就绪的事件
while (selector.select() > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) {
SocketChannel scChannel = ssChannel.accept();
scChannel.configureBlocking(false);
// 监控读就绪状态
scChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 获取当前选择器上“读就绪状态”的通道
SocketChannel sChannel = (SocketChannel) selectionKey.channel();
// 读取数据
ByteBuffer buf = ByteBuffer.allocate(1024);
int len = 0;
while ((len = sChannel.read(buf)) != -1) {
buf.flip();
System.out.println(new String(buf.array()));
buf.clear();
}
}
// 取消选择键
iterator.remove();
}
}
}