BIO、NIO实战
BIO
BIO:blocking IO,分别写一个服务端和客户端交互的C/S实例。
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset; /**
* Created by atai on 2019/3/19.
*/
public class BIOServer { private String host; private int port; private static Charset charset = Charset.forName("UTF-8"); public static void main(String[] args) {
int port = 9010;
try (ServerSocket ss = new ServerSocket(port)) {
while (true) {
Socket s = ss.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream(), charset)); String mess = null;
while ((mess = reader.readLine()) != null) {
System.out.println(mess);
}
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.Scanner; /**
* Created by atai on 2019/3/19.
*/
public class BIOClient implements Runnable { private String host; private int port; private Charset charset = Charset.forName("UTF-8"); public BIOClient(String host, int port) {
super();
this.host = host;
this.port = port;
} @Override
public void run() {
try (Socket s = new Socket(host, port); OutputStream out = s.getOutputStream();) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String mess = scanner.nextLine();
out.write(mess.getBytes(charset));
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
BIOClient client = new BIOClient("localhost", 9010);
client.run();
}
}
启动时,记得先启动服务器代码,才能正常启动客户端代码,不然客户端会报连接异常(不存在可用端口号)。
上面的服务器端代码每次只能同时受理一个客户端请求,其他客户端此时只能等待,为了让服务端支持处理多个客户端请求,可以改造成多线程形式:
public class BIOServerV2 {
private static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) {
int port = 9010;
try (ServerSocket ss = new ServerSocket(port)) {
while (true) {
new Thread(new SocketProcess(ss.accept())).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class SocketProcess implements Runnable {
Socket s;
public SocketProcess(Socket s) {
super();
this.s = s;
}
@Override
public void run() {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream(), charset))) {
String mess = null;
while ((mess = reader.readLine()) != null) {
System.out.println(mess);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
1、理解什么是阻塞
2、思考:阻塞对服务端有什么影响?
3、阻塞时,服务端什么也干不了,不能处理其他客户端的连接,如何改进?
4、多线程
5、如果并发请求量很大,比如一万、十万,会有什么问题?
6、32位系统1个线程对象默认最大需要320KB内存,64位系统默认最大需要1M内存,业务对象也需要内存,内存会不足。过多的线程需要OS频繁切换,也会大大影响性能。
7、怎么办?
8、线程池
既然使用线程池可以避免频繁创建、销毁、切换线程,那就写一个使用线程池的服务端实现:
public class BIOServerV3 {
private static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) {
int port = 9010;
int threads = 100;
ExecutorService tpool = Executors.newFixedThreadPool(threads);
try (ServerSocket ss = new ServerSocket(port)) {
while (true) {
Socket s = ss.accept();
// 丢到线程池中执行
tpool.execute(new SocketProcess(s));
}
} catch (Exception e) {
e.printStackTrace();
}
tpool.shutdown();
}
static class SocketProcess implements Runnable {
Socket s;
public SocketProcess(Socket s) {
super();
this.s = s;
}
@Override
public void run() {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream(), charset))) {
String mess = null;
while ((mess = reader.readLine()) != null) {
System.out.println(mess);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
9、阻塞对线程池的方式有什么影响?
10、阻塞等待接收客户端的数据时,这段时间占着线程,而池中线程数是有限的,并发量大时,将导致没有线程处理请求,请求的响应时间长,甚至拒绝服务。
11、如果能不阻塞,在没有数据时,就去干点别的事情,有数据了才处理数据那该多好。
这个时候,终于等到NIO闪亮登场。
NIO
NIO:new IO,java1.4开始推出的可非阻塞IO,在java.io包中。特点如下:
1、可解决BIO阻塞的不足;
2、但比BIO学习、使用复杂;
3、可以以阻塞、非阻塞两种方式工作;
4、在非阻塞模式下,可以用少量(甚至一个)线程处理大量的IO连接;
5、Java7推出了NIO.2(又称AIO,即异步IO)

Select选择器:非阻塞模式下,一个选择器可检测多个SelectableChannel,获得为读写等操作准备好的通道,就不需要我们用循环去判断了。

Selector的用法:
1、创建Selector
Selector selector = new Selector.open();
2、将要交给Selector检测的SelectableChannel注册进来
channel.configureBlocking(false); // 注意:一定要设为非阻塞模式
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
channel.register方法的第二个参数指定要selector帮忙监听的就绪操作:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
3、通过Selector来选择就绪的Channel,有三个select方法
int select() // 阻塞直到有就绪的Channel
int select(long timeout) // 阻塞最长多久
int selectNow() // 不阻塞这
这三个方法返回值:就绪的Channel数量
int n = selector.select();
注意:select()方法返回当前的就绪数量。
4、获得就绪的SelectionKey集合(当有就绪的Channel时)
Set<SelectionKey> selectedKey = selector.selectedKeys();
5、处理selectedKeys set(详见后面的服务端代码)
Channel通道:数据的来源或去向目标

1、Channel的实现
FileChannel(只能用于BIO)
DatagramChannel
SocketChannel
SocketChannel
ServerSocketChannel
2、各Channel的API方法
open():创建通道
read(Buffer):从通道中读取数据放入到buffer
write(Buffer):将buffer中的数据写给通道
Buffer缓冲区,数据的临时存放区
ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer等
Buffer的基本使用步骤:
1、调用xxxBuffer.allocate(int)创建Buffer
2、调用put方法往Buffer中写数据
3、调用buffer.flip()将buffer转为读模式
4、读取buffer中的数据
5、清理数据buffer.clear(),整理数据buffer.compact()
Buffer的三个重要属性capacity、position、limit

以下是NIO代码的具体实例。
服务器端:
public class NioServer {
private static Charset charset = Charset.forName("UTF-8");
private static CharsetDecoder decoder = charset.newDecoder();
public static void main(String[] args) throws IOException {
// 创建一个selector
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
int port = 9200;
ssc.bind(new InetSocketAddress(port));
// 2注册到selector
// 设置非阻塞
ssc.configureBlocking(false);
// ssc向selector注册,监听连接到来
ssc.register(selector, SelectionKey.OP_ACCEPT);
// 连接的计数
int connectionCount = 0;
// 极少量线程
int threads = 3;
ExecutorService tpool = Executors.newFixedThreadPool(threads);
while (true) {
// 阻塞等待就绪的事件
int readyChannelCount = selector.select();
if (readyChannelCount == 0) {
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
ServerSocketChannel ssssc = (ServerSocketChannel) key.channel();
// 接收连接
SocketChannel cc = ssssc.accept();
// 请selectoror帮忙监测数据到了没
cc.configureBlocking(false);
// 向selector注册
cc.register(selector , SelectionKey.OP_READ, ++connectionCount);
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
// 交给线程池去处理数据读
tpool.execute(new SocketProcess(key));
// 取消Selector注册,防止线程池处理不及时,重复选择
key.cancel();
} else if (key.isWritable()) {
// a channel is ready for writing
}
// 处理后,一定要从selectedKey集合中移除
keyIterator.remove();
}
}
}
static class SocketProcess implements Runnable {
SelectionKey key;
public SocketProcess(SelectionKey key) {
super();
this.key = key;
}
@Override
public void run() {
try {
System.out.println("连接" + key.attachment() + " 发来了:" + readFromChannel());
key.channel().close();
} catch (Exception e) {
e.printStackTrace();
}
}
private String readFromChannel() throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
int bfsize = 1024;
ByteBuffer rbf = ByteBuffer.allocateDirect(bfsize);
// 定义一个更大的buffer
ByteBuffer bigBf = null;
// 读的次数
int count = 0;
while ((sc.read(rbf) != -1)) {
count++;
ByteBuffer temp = ByteBuffer.allocateDirect(bfsize * (count + 1));
if (bigBf != null) {
// 将buffer由写转为读模式
bigBf.flip();
temp.put(bigBf);
}
bigBf = temp;
// 将这次读到的数据放入大buffer
rbf.flip();
bigBf.put(rbf);
// 为了下次读,清理Buffer
rbf.clear();
}
if (bigBf != null) {
bigBf.flip();
try {
// 将字节转为字符,返回接收到的字符串
return decoder.decode(bigBf).toString();
} catch (CharacterCodingException e) {
e.printStackTrace();
}
}
return null;
}
}
}
客户端:
public class NioClient {
private static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) {
try (SocketChannel sc = SocketChannel.open()) {
boolean connected = sc.connect(new InetSocketAddress("localhost", 9200));
System.out.println("connected=" + connected);
// 写
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String mess = scanner.nextLine();
ByteBuffer bf = ByteBuffer.wrap(mess.getBytes(charset));
while (bf.hasRemaining()) {
int writedCount = sc.write(bf);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
BIO、NIO实战的更多相关文章
- IO回忆录之怎样过目不忘(BIO/NIO/AIO/Netty)
有热心的网友加我微信,时不时问我一些技术的或者学习技术的问题.有时候我回微信的时候都是半夜了.但是我很乐意解答他们的问题.因为这些年轻人都是很有上进心的,所以在我心里他们就是很优秀的,我愿意多和努力的 ...
- (转)也谈BIO | NIO | AIO (Java版)
原文地址: https://my.oschina.net/bluesky0leon/blog/132361 关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一 ...
- Tomcat Connector三种运行模式(BIO, NIO, APR)的比较和优化
Tomcat Connector的三种不同的运行模式性能相差很大,有人测试过的结果如下: 这三种模式的不同之处如下: BIO: 一个线程处理一个请求.缺点:并发量高时,线程数较多,浪费资源. Tomc ...
- 拿搬东西来解释udp tcpip bio nio aio aio异步
[群主]雷欧纳德简单理解 tcpip是有通信确认的面对面通信 有打招呼的过程 有建立通道的过程 有保持通道的确认 有具体传输udp是看到对面的人好像在对面等你 就往对面扔东西[群主]雷欧 ...
- 也谈BIO | NIO | AIO (Java版--转)
关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一个解释: BIO | NIO | AIO,本身的描述都是在Java语言的基础上的.而描述IO,我们需要从两个 ...
- tomcat bio nio apr 模式性能测试
转自:tomcat bio nio apr 模式性能测试与个人看法 11.11活动当天,服务器负载过大,导致部分页面出现了不可访问的状态.那后来主管就要求调优了,下面是tomcat bio.nio.a ...
- Netty5序章之BIO NIO AIO演变
Netty5序章之BIO NIO AIO演变 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠的网络服务器和客户端程序.Netty简化了网络程序的开发,是很多框架和公司都在使 ...
- I/O模型系列之三:IO通信模型BIO NIO AIO
一.传统的BIO 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请 ...
- BIO,NIO与AIO的区别
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理.Java AIO(NIO.2 ...
- 【netty】(1)---BIO NIO AIO演变
BIO NIO AIO演变 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠的网络服务器和客户端程序.Netty简化了网络程序的开发,是很多框架和公司都在使用的技术. Net ...
随机推荐
- CSS实现输入框宽度随内容自适应效果
有时候我们会遇到如下需求:输入框的宽度随内容长度自适应,当输入框宽度增大到一定值时,里边的内容自动隐藏. 面对这种需求,我们首先想到的是使用input元素标签,但是发现input标签的宽度默认设定的是 ...
- 利用python操作excel
https://zhuanlan.zhihu.com/p/51292549 打开程序:https://segmentfault.com/q/1010000002441500
- 无头浏览器phantomJS
selenium: 有头浏览器的代表(selenium+python也可实现静默运行 引入python的一个包,包叫:虚拟屏幕pyvirtualdisplay) PhantomJS : 无头浏览器的代 ...
- 雷林鹏分享:jQuery EasyUI 数据网格 - 合并单元格
jQuery EasyUI 数据网格 - 合并单元格 数据网格(datagrid)经常需要合并一些单元格.本教程将向您展示如何在数据网格(datagrid)中合并单元格. 为了合并数据网格(datag ...
- 文献导读 | A Pan-Cancer Analysis of Enhancer Expression in Nearly 9000 Patient Samples
Chen, H., Li, C., Peng, X., Zhou, Z., Weinstein, J.N., Liang, H. and Cancer Genome Atlas Research Ne ...
- 『PyTorch』第十五弹_torch.nn.Module的属性设置&查询
一.背景知识 python中两个属相相关方法 result = obj.name 会调用builtin函数getattr(obj,'name')查找对应属性,如果没有name属性则调用obj.__ge ...
- Container&injection
容器(Container)就是组件和底层服务细节之间的接口.在web组件.企业级Bean等能够执行之前,它必须被装配为一个JavaEE模块,并部署在容器上. 在JavaEE5时代通过注解的方式注入(i ...
- MQTT简介
MQTT简介 MQTT是IBM开发的一个即时通讯协议,该协议支持所有的平台,几乎可以把所有联网的物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议 MQTT的特 ...
- vue2.0 实现富文本编辑器功能
前端富文本编译器使用总结: UEditor:百度前端的开源项目,功能强大,基于 jQuery,但已经没有再维护,而且限定了后端代码,修改起来比较费劲 bootstrap-wysiwyg:微型,易用,小 ...
- decltype typename
decltype((variable))总是引用类型,但是decltype(variable)只有当variable是引用类型时才是引用类型. #include <iostream> #i ...