Java IO学习笔记五:BIO到NIO
作者: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快很多。
源码
Java IO学习笔记五:BIO到NIO的更多相关文章
- Java IO学习笔记五
管道流 管道流的主要作用是可以进行两个线程间的通讯,分为管道输出流(PipedOutputStream).管道输入流(PipedInputStream),如果想要进行管道输出,则必须要把输出流连在输入 ...
- Java IO学习笔记六:NIO到多路复用
作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...
- Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer
作者:Grey 原文地址:Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer ByteBuffer.allocate()与ByteBuffer.allocateD ...
- Java IO学习笔记三:MMAP与RandomAccessFile
作者:Grey 原文地址:Java IO学习笔记三:MMAP与RandomAccessFile 关于RandomAccessFile 相较于前面提到的BufferedReader/Writer和Fil ...
- Java IO学习笔记四:Socket基础
作者:Grey 原文地址:Java IO学习笔记四:Socket基础 准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为: io1实例:192.168.205.138 io ...
- Java IO学习笔记八:Netty入门
作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...
- Java IO学习笔记:概念与原理
Java IO学习笔记:概念与原理 一.概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...
- Java IO学习笔记总结
Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...
- Java IO学习笔记三
Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...
随机推荐
- Caddy-基于go的微型serve用来做反向代理和Gateway
1.简单配置 2.go实现,直接一个二进制包,没依赖. 3.默认全站https 常用 反向代理,封装多端口gateway 使用:启动直接执行二进制文件 caddy 就行 根据输出信息 直接https: ...
- 游戏中的2D OBB碰撞模型的碰撞算法介绍和实践
前言 上一篇博文说道,射线与场景中模型上的所有三角形求交时,会大幅度影响效率且花费比较多的时间,因此会采取使用包围盒的形式,进行一个加速求交.在此文中介绍OBB碰撞模型的碰撞算法 OBB的碰撞模型 有 ...
- C#可空类型及其衍生运算符
这节讲一下C#可空类型(Nullable) 我们知道,值类型在使用前必须设置值,而引用类型则可以是null,但在某些情况下,为值类型设置为空是必要的(如处理数据库数据的时候),微软因此推出了可空类型 ...
- Go的Waitgroup和锁
学 Go 的时候知道 Go 语言支持并发,最简单的方法是通过 go 关键字开启 goroutine 即可.可在工作中,用的是 sync 包的 WaitGroup,然而这样还不够,当多个 gorouti ...
- 发布声明$\beta$
一.新功能 \(\beta\)阶段集中开发了3大核心功能:支持模块的嵌套.模型市场.模型推理,这三项基本上都是从零开始.徒手开发的功能,没有轮子可以参照,因此也不具有可以对比的先前版本. 除此之外,开 ...
- [MySQL数据库之数据库相关概念、MySQL下载安装、MySQL软件基本管理、SQL语句]
[MySQL数据库之数据库相关概念.MySQL下载安装.MySQL软件基本管理.SQL语句] 数据库相关概念 数据库管理软件的由来 数据库管理软件:本质就是个C/S架构的套接字程序. 我们在编写任何程 ...
- spec cpu2006 官网
https://www.spec.org/cpu2006/Docs/install-guide-unix.html
- Python re 截取文本中IP地址及用户名
文本示例: ts=2019-07-10T06:43:06523942Z pid=1875 tid=6320 version=e73c536 proto=http id=5a61a613e395f883 ...
- VMware(Caps Lock键)切换大小写作用失效的Bug的解决办法
前言 第一种情况是:进入VMware虚拟机的时候,即使按了Capslock键开启大写,灯虽然亮了,但是,打出来的字母还是小写,没有有任何的效果,根本不能转换成大写. 只有按Shift+字母才能输入大写 ...
- jsp表单传值后在页面中文显示乱码
主要通过添加以下代码来实现(前提是其他处的编码都设置成了utf-8): response.setContentType("text/html;charset=utf-8"); re ...