IO

服务端ServerSocket 客户端Socket

缺点每次客户端建立连接都会另外启一个线程处理。读取和发送数据都是阻塞式的。

如果1000个客户端建立连接将会产生1000个线程

Server端
package bhz.bio.test;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; public class Server {
private int port;
private ServerSocket serverSocket; public Server(int port) {
this.port = port; } public void start() throws IOException {
try {
serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();// 阻塞等待客户端建立连接
new Thread(new ServerHandler(socket)).start();//另外起一個線程處理客戶端的請求
}
} catch (IOException e) {
e.printStackTrace();
// TODO Auto-generated catch block
System.out.println("服务器启动失败");
}finally {
if(serverSocket!=null) {
serverSocket.close();//释放资源操作
} } }
}
ServerHandler
package bhz.bio.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket; public class ServerHandler implements Runnable {
private Socket socket; public ServerHandler(Socket socket) {
this.socket = socket;
} @Override
public void run() {
// TODO Auto-generated method stub BufferedReader in = null;
PrintWriter out = null;
try {
while (true) {
// 监听客户端的发送消息
out = new PrintWriter(socket.getOutputStream(),true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String content = in.readLine(); System.out.println("接收到客户端发送的消息:" + content);//
out.println("哈哈哈");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
socket = null;
} } }
客户端
package bhz.bio.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException; public class Client {
private String ip;
private int port;
private Socket socket;
private PrintWriter out;
private BufferedReader in;
public Client(String ip, int port) {
this.ip = ip;
this.port = port;
} public void connect() {
try {
socket = new Socket(ip, port);
out = new PrintWriter(socket.getOutputStream(),true);
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (Exception e) {
// TODO: handle exception
System.out.println("建立連接失敗");
} } public void send(String message) throws IOException { out.println(message); try {
String content=in.readLine();
System.out.println("接收到服务端的数据:"+content);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} }
}

测试类

package bhz.bio.test;

import java.io.IOException;

public class mainTest {
public static void main(String[] args) {
//启动服务端
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
Server server=new Server(8089);
try {
server.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}).start(); new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
Client client=new Client("127.0.0.1", 8089);
client.connect();//与服务器建立连接
try {
client.send("你好呀");
client.send("你好呀2");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}

输出

接收到客户端发送的消息:你好呀
接收到服务端的数据:哈哈哈
接收到客户端发送的消息:你好呀2
接收到服务端的数据:哈哈哈
使用线程池限制客户端数量(伪异步)
public class Server {
private int port;
private ServerSocket serverSocket; private ExecutorService executorService;
public Server(int port) {
this.port = port;
executorService=Executors.newFixedThreadPool(50);//限制同时在线客户端数量为10个 } public void start() throws IOException {
try {
serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();// 阻塞等待客户端建立连接
executorService.execute(new ServerHandler(socket));//线程池处理
}
} catch (IOException e) {
e.printStackTrace();
// TODO Auto-generated catch block
System.out.println("服务器启动失败");
}finally {
if(serverSocket!=null) {
serverSocket.close();//释放资源操作
}
} }
}

Nio

nio改善了io一个客户端建立连接就创建一个线程监听请求的模型。

nio的核心概念

1.Channel(   负责读写数据 可以channel读取数据到buffer也可以通过channel写入数据到channel)

  • FileChannel
  • DatagramChannel  能通过UDP读写网络中的数据
  • SocketChannel 能通过TCP读写网络中的数据。
  • ServerSocketChannel 可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

2.Buffers

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

存储数据的容器

3.Selectors

负责轮询Channel的状态进行对应的操作。发送消息和建立连接都需要往selectors注册channel (一个selectors可以监听无数个channel状态)

buffer的使用

建立一个指定大小的缓冲区

private ByteBuffer readBuf = ByteBuffer.allocate(1024);

往buffer容器添加数据内容position字段都会累加。

读取数据是根据position字段来读取

所以读取之前要记得重置position位置为0

readBuf.flip();

读取缓冲区里面数据

//根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
/ 接收缓冲区数据
this.readBuf.get(bytes);
//打印结果
String body = new String(bytes).trim();

服務端

package bhz.nio.test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set; public class Server { private int port;
private ServerSocketChannel serverSocketChannel;
private Selector selector;
//2 建立缓冲区
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
public Server(int port) {
this.port = port;
}
public void start() throws IOException {
selector=Selector.open();//创建选择器
serverSocketChannel=ServerSocketChannel.open();//创建监听通道
serverSocketChannel.configureBlocking(false);//开启非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(port));//绑定端口
//把服务器通道注册到多路复用器上,并且监听阻塞事件
serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
//轮询多路复用器
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
//阻塞,只有当至少一个注册的事件发生的时候才会继续.
try {
selector.select();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 返回多路复用器已经选择的结果集
Iterator<SelectionKey> selectionKeys=selector.selectedKeys().iterator();
//3 进行遍历
while(selectionKeys.hasNext()){
//4 获取一个选择的元素
SelectionKey key = selectionKeys.next();
//5 直接从容器中移除就可以了
selectionKeys.remove();
//6 如果是有效的
if(key.isValid()){
//7 如果为阻塞状态
if(key.isAcceptable()){
accept(key);
}
//8 如果为可读状态
if(key.isReadable()){
read(key);
}
//9 写数据
if(key.isWritable()){
//this.write(key); //ssc
}
} } } } }).start();
} private void accept(SelectionKey key) {
try {
//1 获取服务通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//2 执行阻塞方法
SocketChannel sc = ssc.accept();
//3 设置阻塞模式
sc.configureBlocking(false);
//4 注册到多路复用器上,并设置读取标识
sc.register(this.selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
private void read(SelectionKey key) {
try {
//1 清空缓冲区旧的数据
this.readBuf.clear();
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(this.readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
this.readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
//7 接收缓冲区数据
this.readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("Server : " + body); // 9..可以写回给客户端数据 } catch (IOException e) {
e.printStackTrace();
} } }

客戶端

package bhz.nio.test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator; public class Client { private SocketChannel socketChannel;
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
private Selector selector;
public void connect(String ip,int port) throws IOException {
selector=Selector.open();//创建选择器 socketChannel=SocketChannel.open();//创建连接 if(socketChannel.connect(new InetSocketAddress(ip,port)));
else socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT); //注册到多路复用器
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
SelectionKey key = null;
while (true) {
try {
selector.select();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Iterator<SelectionKey> selectionKeys=selector.selectedKeys().iterator(); while (selectionKeys.hasNext()) {
key=selectionKeys.next();
if(key.isReadable()) { } } } }
}).start();
}
private void read(SelectionKey key) {
try {
//1 清空缓冲区旧的数据
this.readBuf.clear();
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(this.readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
this.readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
//7 接收缓冲区数据
this.readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("client : " + body); // 9..可以写回给客户端数据 } catch (IOException e) {
e.printStackTrace();
} } public void write(String message) throws IOException {
byte[] bytes =message.getBytes(); //把数据放到缓冲区中
writeBuf.put(bytes);
//对缓冲区进行复位
writeBuf.flip();
//写出数据
socketChannel.write(writeBuf);
//清空缓冲区数据
writeBuf.clear();
}
}

測試類

package bhz.nio.test;

import java.io.IOException;

public class TestMain {
public static void main(String[] args) throws InterruptedException {
final Server server = new Server(8088);
final Client client=new Client(); new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
try {
server.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }).start();
Thread.sleep(2000);
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
try {
client.connect("127.0.0.1", 8088);
client.write("哈哈哈哈");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}).start();
}
}

AIO

服務端類AsynchronousServerSocketChannel

客戶端類AsynchronousSocketChannel

服务端

public class Server {

    private int port;
//服务器通道
AsynchronousServerSocketChannel serverSocketChannel;
//线程组
private AsynchronousChannelGroup threadGroup;
private ExecutorService executorService;
public Server(int port) {
this.port=port; }
public void start() throws IOException {
//创建线程池
executorService=Executors.newCachedThreadPool();
//创建线程组
threadGroup = AsynchronousChannelGroup
.withThreadPool(executorService);
//创建服务器通道
serverSocketChannel = AsynchronousServerSocketChannel.open(threadGroup);
//进行绑定
serverSocketChannel.bind(new InetSocketAddress(port)); serverSocketChannel.accept(this, new ServerHandler());
}
}

异步读handle

/**
* 客户端接收服务器数据回调
* @author Administrator
*
*/
public class ClientReadHandler implements CompletionHandler<Integer, ByteBuffer> { @Override
public void completed(Integer result, ByteBuffer attachment) {
// TODO Auto-generated method stub
attachment.flip();//读模式 position设置为0
byte[] datas=new byte[attachment.remaining()];
attachment.get(datas);
try {
System.out.println("服务端响应"+new String(datas,"utf-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } @Override
public void failed(Throwable exc, ByteBuffer attachment) {
// TODO Auto-generated method stub
System.out.println("客户端读取失败"); } }

客户端

package bnz.aio.test;

import java.io.IOException;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler; public class Client implements CompletionHandler<Void, Client> {
private String ip;
private int port;
private AsynchronousSocketChannel asc; public Client(String ip, int port) {
this.ip = ip;
this.port = port;
} public void connect() throws IOException {
asc = AsynchronousSocketChannel.open();
asc.connect(new InetSocketAddress(ip, port), this, this);
//读取数据
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
asc.read(readBuffer,readBuffer,new ClientReadHandler());//服务器写入数据回调
} public void Writer(String message) {
byte[] req = message.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
//异步写
asc.write(writeBuffer,writeBuffer, new WriteHandler(asc));
}
//连接成功的回调 @Override
public void completed(Void result, Client attachment) {
// TODO Auto-generated method stub
System.out.println("建立连接成功"); } //异常回调
@Override
public void failed(Throwable exc, Client attachment) {
// TODO Auto-generated method stub
System.out.println("建立连接失败"); }
}

读handle

/**
* 客户端接收服务器数据回调
* @author Administrator
*
*/
public class ClientReadHandler implements CompletionHandler<Integer, ByteBuffer> { @Override
public void completed(Integer result, ByteBuffer attachment) {
// TODO Auto-generated method stub
attachment.flip();//读模式 position设置为0
byte[] datas=new byte[attachment.remaining()];
attachment.get(datas);
try {
System.out.println("服务端响应"+new String(datas,"utf-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } @Override
public void failed(Throwable exc, ByteBuffer attachment) {
// TODO Auto-generated method stub
System.out.println("客户端读取失败"); } }

写handle

/**
* 客户端异步写回调
* @author Administrator
*
*/
public class WriteHandler implements CompletionHandler<Integer, ByteBuffer> { private AsynchronousSocketChannel channel;
public WriteHandler(AsynchronousSocketChannel asynchronousSocketChannel) {
// TODO Auto-generated constructor stub
this.channel=asynchronousSocketChannel;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
// TODO Auto-generated method stub
attachment.hasRemaining();
channel.write(attachment,attachment,this);
} @Override
public void failed(Throwable exc, ByteBuffer attachment) {
// TODO Auto-generated method stub
System.out.println("数据写入失败");
} }

io nio  aio比较

io 同步阻塞 jdk4以前的网络通信模型,io一个连接需要开启一个线程处理 ,在大量连接情况下服务器会创建N个线程。导致cpu线程调度频繁 性能底下 (同步阻塞)

nio 同步非阻塞  jdk4之后的网络通信,主要是为了解决io模型下一个线程处理一个连接的性能问题, 引入事件驱动。l客户端连接服务端注册到复用器,然后轮询复用器注册连接的状态。只需要一个线程处理   (同步非阻塞)

aio 异步非阻塞 jdk7推出的网络通信框架 跟nio不同的是 读写都是异步的(完全交给内核处理)  完成后通知应用程序

同步阻塞 可以理解为买票排队。没买到票之前什么也不能做。

同步非阻塞 可以理解为邮局。 经常需要跑去邮局去询问有没有自己的邮件。

异步非阻塞 可以理解为去饭店点餐 然后回家 饭店做好打包后 送上门

应用场景

io可以应用在少量且固定连接的网络通信  应为io的模型更简单容易理解

nio 在大量连接情况使用

aio在大量连接 且读写数据非常大的情况下

java IO(BIO)、NIO、AIO的更多相关文章

  1. Java的BIO,NIO,AIO

    Java中的IO操作可谓常见.在Java的IO体系中,常有些名词容易让人困惑不解.为此,先通俗地介绍下这些名词. 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同 ...

  2. 【Java】 BIO与NIO以及AIO分析

    一.BIO与NIO以及AIO的概念 BIO是同步阻塞式的IO NIO是同步非阻塞的IO (NIO1.0,JDK1.4) AIO是非同步非阻塞的IO(NIO2.0,JDK1.7) 二.BIO简单分析 1 ...

  3. Java中BIO,NIO,AIO的理解

    在高性能的I/O体系设计中,有几个概念常常会使我们感到迷惑不解.具体如下: 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步非阻塞? 7  ...

  4. JAVA中IO技术:BIO、NIO、AIO

    1.同步异步.阻塞非阻塞概念        同步和异步是针对应用程序和内核的交互而言的. 阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作 ...

  5. Java IO 之 BIO、NIO、AIO

    1.BIO.NIO.AIO解释 Java BIO : 同步并阻塞 (Blocking IO) 一个连接一个线程 即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不 ...

  6. Java中的IO、NIO、File、BIO、AIO详解

    java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?         Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包 ...

  7. 京东数科二面:常见的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥区别?

    IO 模型这块确实挺难理解的,需要太多计算机底层知识.写这篇文章用了挺久,就非常希望能把我所知道的讲出来吧!希望朋友们能有收货!为了写这篇文章,还翻看了一下<UNIX 网络编程>这本书,太 ...

  8. 京东数科面试真题:常见的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥区别?

    本文节选自<Java面试进阶指北 打造个人的技术竞争力> 面试中经常喜欢问的一个问题,因为通过这个问题,面试官可以顺便了解一下你的操作系统的水平. IO 模型这块确实挺难理解的,需要太多计 ...

  9. Java IO模型:BIO、NIO、AIO

    Java IO模型:BIO.NIO.AIO 本来是打算直接学习网络框架Netty的,但是先补充了一下自己对Java 几种IO模型的学习和理解.分别是 BIO.NIO.AIO三种IO模型. IO模型的基 ...

  10. 深入分析JAVA IO(BIO、NIO、AIO)

    IO的基本常识 1.同步 用户进程触发IO操作并等待或者轮询的去查看IO操作是否完成 2.异步 用户触发IO操作以后,可以干别的事,IO操作完成以后再通知当前线程继续处理 3.阻塞 当一个线程调用 r ...

随机推荐

  1. luogu2765 魔术球问题 网络流

    题目大意: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球.(1)每次只能在某根柱子的最上面放球.(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数.试设计一 ...

  2. oc33--构造方法2

    // Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @property int age; ...

  3. MTK camera 闪光灯Flashlight驱动调试流程

    MTK camera 闪光灯Flashlight驱动调试流程 分类: MtkDev  |  作者: topicdev 相关  |  发布日期 : 2014-09-26  |  热度 : 153°   ...

  4. DirectFB编程【转】

    本文转载自:http://www.cnblogs.com/274914765qq/p/4358088.html DirectFB编程 一.简介 DirectFB是一个轻量级的提供硬件图形加速,输入设备 ...

  5. 洛谷 P1032 [ NOIP 2002 ] 字串变换 —— 字符串+bfs

    题目:https://www.luogu.org/problemnew/show/P1032 字符串好复杂...先写了个 dfs ,RE一个点TLE一个点,不知该怎么改了... #include< ...

  6. Coursera Algorithms week1 算法分析 练习测验: 3Sum in quadratic time

    题目要求: Design an algorithm for the 3-SUM problem that takes time proportional to n2 in the worst case ...

  7. form内部的button_to不submit

    创建: 2017/09/12 更新: 2018/03/17 修正因为博客迁移造成的格式问题 官方文档 http://railsdoc.com/references/button_to 参考文档 htt ...

  8. php中curl的详细解说 【转载】

    这几天在帮一些同学处理问题的时候,突然发现这些同学是使用file_get_contents()函数来采集页面内容的,貌似都没有curl的概念亦或是对这种工具特别不敏感, 本文我来给大家详细介绍下cUR ...

  9. python2.X现在不能安装Django了:Collecting django Using cached Django-2.0.tar.gz

    使用pip安装django2: pip install django 报错: Collecting django  Using cached Django-2.0.tar.gz    Complete ...

  10. struts2OGNL表达式(三)

    OGNL表达式 OGNL对象试图导航语言.${user.addr.name}这种写法就叫对象试图导航.Struts框架使用OGNL作为默认的表达式语言 OGNL不仅仅可以试图导航,支持比EL表达式更加 ...