作者:Grey

原文地址: Java IO学习笔记五:BIO到NIO

准备环境

准备一个CentOS7的Linux实例:

实例的IP:

192.168.205.138

我们这次实验的目的就是直观感受一下Socket编程中BIO模型和NIO模型的性能差异

BIO

准备服务端代码:

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket; /**
* BIO Socket Server
*/
public class SocketServerBIOTest {
private static final int PORT = 9090;
private static final int BACK_LOG = 2; public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket();
server.bind(new InetSocketAddress(PORT), BACK_LOG);
System.out.println("server started , port : " + PORT);
} catch (IOException e) {
e.printStackTrace();
}
try {
// 接受客户端连接
while (true) {
// 先阻塞,这样客户端暂时无法连接进来
// System.in.read(); // 这个方法也是阻塞的,如果没有客户端连接进来,会一直阻塞在这里,除非设置了超时时间
Socket client = server.accept(); System.out.println("client " + client.getPort() + " connected!!!");
// 客户端连接进来后,开辟一个新的线程去接收并处理
new Thread(() -> {
try {
InputStream inputStream = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
char[] data = new char[1024];
while (true) {
int num = reader.read(data);
if (num > 0) {
System.out.println("client read some data is :" + num + " val :" + new String(data, 0, num));
} else if (num == 0) {
System.out.println("client read nothing!");
continue;
} else {
System.out.println("client read -1...");
System.in.read();
client.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

并且在Linux实例上运行这个代码,然后在自己本地的机器上准备客户端的代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.LinkedList; public class C10Kclient { public static void main(String[] args) {
LinkedList<SocketChannel> clients = new LinkedList<>();
InetSocketAddress serverAddr = new InetSocketAddress("192.168.205.138", 9090);
for (int i = 10000,j = 10001; i < 65000; i+=2,j+=2) {
try {
SocketChannel client1 = SocketChannel.open();
SocketChannel client2 = SocketChannel.open();
client1.bind(new InetSocketAddress("192.168.205.1", i ));
client1.connect(serverAddr);
clients.add(client1); client2.bind(new InetSocketAddress("192.168.205.1", j)); client2.connect(serverAddr);
clients.add(client2); } catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("clients "+ clients.size());
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}

服务端每次用两端口循环多次去和服务端建立连接,我们可以观察服务端建立连接的速度,通过服务端打印的信息可以感知到连接的速度。

[root@io socket]# javac SocketServerBIOTest.java && java SocketServerBIOTest
server started , port : 9090
client 10000 connected!!!
client 10001 connected!!!
client 10002 connected!!!
client 10003 connected!!!
client 10006 connected!!!
client 10007 connected!!!
client 10008 connected!!!
client 10004 connected!!!
client 10009 connected!!!
client 10010 connected!!!
client 10011 connected!!!
...

NIO

我们把服务端的BIO切换成NIO,服务端的代码改成如下:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList; public class SocketServerNIOTest { public static void main(String[] args) throws Exception { LinkedList<SocketChannel> clients = new LinkedList<>(); ServerSocketChannel ss = ServerSocketChannel.open(); //服务端开启监听:接受客户端
ss.bind(new InetSocketAddress(9090));
ss.configureBlocking(false); while (true) { SocketChannel client = ss.accept();
if (client == null) { } else {
client.configureBlocking(false); //重点 socket(服务端的listen socket<连接请求三次握手后,往我这里扔,我去通过accept 得到 连接的socket>,连接socket<连接后的数据读写使用的> )
int port = client.socket().getPort();
System.out.println("client..port: " + port);
clients.add(client);
} ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
//遍历已经链接进来的客户端能不能读写数据
for (SocketChannel c : clients) {
int num = c.read(buffer);
if (num > 0) {
buffer.flip();
byte[] aaa = new byte[buffer.limit()];
buffer.get(aaa); String b = new String(aaa);
System.out.println(c.socket().getPort() + " : " + b);
buffer.clear();
}
}
}
}
}

其中

ss.configureBlocking(false);

即把服务端设置为非阻塞的,由于非阻塞,所以死循环中,代码不会卡在:

SocketChannel client = ss.accept();

这里一直不执行,而且也无须抛出一个新的线程去接收客户端。

当得到

client != null

的时候,即有新的客户端连接进来,我们把这个clients加入到列表中,然后遍历clients,去消费客户端的请求。

同时,我们可以在服务端设置客户端的非阻塞,即:

client.configureBlocking(false);

再次运行客户端,并切换到服务端查看打印日志,速度比前面的BIO快了非常多。

[root@io socket]# javac SocketServerNIOTest.java && java SocketServerNIOTest
... 速度快很多...
client..port: 10000
client..port: 10001
client..port: 10002
client..port: 10003
client..port: 10004
client..port: 10005
client..port: 10006
client..port: 10007
client..port: 10008
client..port: 10009
client..port: 10010
client..port: 10011
...

为什么BIO慢

因为每次连接都会发生两次系统调用,一次是通过accept建立socket,另一次是调用clone方法抛出一个线程。

而NIO只有一个线程(线程克隆的耗时就不存在了),而且操作系统也提供了对应的支持。所以要比BIO快很多。

源码

Github

Java IO学习笔记五:BIO到NIO的更多相关文章

  1. Java IO学习笔记五

    管道流 管道流的主要作用是可以进行两个线程间的通讯,分为管道输出流(PipedOutputStream).管道输入流(PipedInputStream),如果想要进行管道输出,则必须要把输出流连在输入 ...

  2. Java IO学习笔记六:NIO到多路复用

    作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...

  3. Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer

    作者:Grey 原文地址:Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer ByteBuffer.allocate()与ByteBuffer.allocateD ...

  4. Java IO学习笔记三:MMAP与RandomAccessFile

    作者:Grey 原文地址:Java IO学习笔记三:MMAP与RandomAccessFile 关于RandomAccessFile 相较于前面提到的BufferedReader/Writer和Fil ...

  5. Java IO学习笔记四:Socket基础

    作者:Grey 原文地址:Java IO学习笔记四:Socket基础 准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为: io1实例:192.168.205.138 io ...

  6. Java IO学习笔记八:Netty入门

    作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...

  7. Java IO学习笔记:概念与原理

    Java IO学习笔记:概念与原理   一.概念   Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...

  8. Java IO学习笔记总结

    Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...

  9. Java IO学习笔记三

    Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...

随机推荐

  1. C#/VB.NET 自定义动画路径

    PPT中的动画效果可分为已有内置动画以及自定义动画.设置内置动画,只需直接指定动画效果类型即可.本文主要介绍如何实现自定义动画,即自定义形状动作线性路径.附C#及VB.NET代码供参考. 程序运行环境 ...

  2. (CV学习笔记)Attention

    Attention(注意力机制) Attention for Image Attention for Machine Translation Self-Attention 没有image-Attent ...

  3. 手写一个LRU工具类

    LRU概述 LRU算法,即最近最少使用算法.其使用场景非常广泛,像我们日常用的手机的后台应用展示,软件的复制粘贴板等. 本文将基于算法思想手写一个具有LRU算法功能的Java工具类. 结构设计 在插入 ...

  4. Redis 集群伸缩原理

    Redis 节点分别维护自己负责的槽和对应的数据.伸缩原理:Redis 槽和对应数据在不同节点之间移动 环境:CentOS7 搭建 Redis 集群 一.集群扩容 1. 手动扩容 (1) 准备节点 9 ...

  5. C#中的委托(Update)

    什么是委托? 委托(delegate)是一种托管方法的数据结构,它是一种引用类型,是对方法的引用.如果说int,string等是对数据类型的定义,那么委托就类似于对"方法类型"的定 ...

  6. 发布 .NET 5 带运行时单文件应用时优化文件体积的方法

    自 .NET 发布起,.NET Framework 运行环境就是其摆脱不掉的桎梏.后来有了 .NET Core ,微软终于将自带运行时和单文件程序带给了我们.即便如此,大部分情况下开发者仍然不太满意: ...

  7. Redis数据结构—整数集合与压缩列表

    目录 Redis数据结构-整数集合与压缩列表 整数集合的实现 整数集合的升级 整数集合不支持降级 压缩列表的构成 压缩列表节点的构成 小结 Redis数据结构-整数集合与压缩列表 大家好,我是白泽.今 ...

  8. win10 下安卓源码同步小技巧

    win10下,通过 清华镜像源 AOSP 可以快速拿到 100G 的 .repo  备份 然后 用 repo sync 就可以得到 安卓源码,爽不爽! 下载到win10 e盘下,用powershell ...

  9. What are CBR, VBV and CPB?

    转自:https://codesequoia.wordpress.com/2010/04/19/what-are-cbr-vbv-and-cpb/ It's common mistake to to ...

  10. [敏杰开发]知识路书——图形化文献管理大师 Beta版发布喽!!!

    [敏杰开发]知识路书--图形化文献管理大师 Beta版发布喽!!! 一.总览 项目名称:知识路书 发布形式:网页应用 发布地址:http://roadmap.imcoming.top 二.运行环境与使 ...