了解一下简单的概念
阻塞:我们需要等待数据准备好的时候才可以进行其他的操作
非阻塞:我们不需要等待数据准备好之后,我们也可以进行其他的操作
我的的NIO是同步非阻塞的,而我们的BIO是同步阻塞的
了解一下传统的BIO
简要的文字说明:在BIO中,client与server端之间是面向流的,通过留来进行传输的,每个client连接到server端时都会开启一条通道,都有一个Thread来处理,当client断开是,通断换删除掉
这样导致的问题:1.阻塞问题,2.开销问题,3.多线程接受多个请请求
了解一下Java 1.5后出现的NIO
简要的文字说明:在NIO中,client与server的通信是面向留的,而且他是IO多路复用的,他是非阻塞的,不会让你阻塞在channel上面,只要你有消息过来,他都会只通过一个死循环线程,不断的轮询,然后把他塞到buffer缓冲区里面,不需要等待数据准备完了,才进行其他的操作。单线程接受多个请求
传统BIO的代码实现
client端:
package com.bio;
import java.io.OutputStream;
import java.net.Socket;
/**
-
传统的Bio的client端
-
@author michael
-
@date 2019-03-28 08:52
*/
public class BioClient {/**
- main
- @param args
*/
public static void main(String[] args) throws Exception {
BioClient bioClient = new BioClient();
bioClient.setBioClient();
}
private void setBioClient() throws Exception {
//创建一个sooket相当于建好一条乡村通道,需要是才创建
Socket client = new Socket(“127.0.0.1”, 8089);
//要往这条通道塞东西,就拿到这条通道的OutputStream
OutputStream outputStream = client.getOutputStream();
//我们要输出的内容
String name = “Michael”;
//把数据塞到流里面
outputStream.write(name.getBytes());
//关闭流
outputStream.close();
//关闭socket
client.close();
}
}
server端:
package com.bio;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
-
传统BIO-server端
-
@author michael
-
@date 2019-03-28 09:11
*/
public class BioServer {ServerSocket server;
//服务器
public BioServer(int port) {
try {
//把Socket服务端启动,socket的服务端,new ServerSocket(int port)
server = new ServerSocket(port);
System.out.println(“BIO服务已启动,监听端口是:” + port);
} catch (IOException e) {
e.printStackTrace();
}
}/**
- 监听
- @throws Exception
*/
public void listener() throws Exception {
//使用死循环,不断的获取client的数据
while (true) {
//由server的accpt方法获取client,该方法是阻塞的,由server获取到乡村通道
Socket client = server.accept();
//对于server而言,我们要从乡村通道里面获取到需要塞进来的东西
InputStream inputStream = client.getInputStream();
//缓冲区,数组而已
byte[] buff = new byte[1024];
//把东西放到缓冲区里面,然后输出
int len = inputStream.read(buff);
//判断
if (len > 0) {
//缓冲转换字符串
String name = new String(buff, 0, len);
System.out.println(name);
}
}
}
/**
- 主函数
- @param args
*/
public static void main(String[] args) throws Exception {
new BioServer(8089).listener();
}
}
总结:
client
1.在client连接server端的时候,我们首先需要建设打开一条通道,new Socket(“IP”,port);
2.然后我们需要从通道获取输出流,才能把数据输出到通道,client.getOutputStream();
3.接着我们把需要的内容写到流里面,outputStream.write(“xxxxx”)
4.最后我们需要关闭流,关闭通道
思想,客户端创建通道,从通道里回去流,流里放数据
server
1.在server端,首先我们要根据port创建serverSocket;
2.接着我们要死循环,不断的获取连接到我们的通道Socket client = serverSocket.accpet();
3.接着获取我们读取的输入流InpustStream =client.getInpustStream()
4. 把东西放到缓冲区里面,然后输出int len = inputStream.read(buff);
思想:根据port创建客户端,循环,不断获取连接到我们身上的client(通道),获取流,然后输出
缺点是:他需要多个线程才能接受多个客户端,且他的通道没办法复用,用完之后他会回收掉
NIO的代码实现
client端:
package com.nio;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
/**
-
NIO客户端
-
@author michael
-
@date 2019-03-28 10:06
*/
public class NIOClient {/**
- NIO传输的高速公路的端口
*/
private final InetSocketAddress inetSocketAddress = new InetSocketAddress(“localhost”, 8089);
/**
- NIO的客户端,对比于传统的而言转换为SocketChannel
*/
private SocketChannel client = null;
/**
- 控制字符串格式
*/
private Charset charset = Charset.forName(“UTF-8”);
/**
- 构造函数
*/
public NIOClient() throws Exception {
//不管什么,先建一条高速公路先
client = SocketChannel.open(inetSocketAddress);
}
/**
- 写数据
- @param name
- @throws Exception
*/
public void write(String name) throws Exception {
//输出数据
client.write(charset.encode(name));
}
/**
- main函数
- @param args
- @throws Exception
*/
public static void main(String[] args) throws Exception {
new NIOClient().write(“Michael”);
}
}
- NIO传输的高速公路的端口
server端:
package com.nio;
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;
/**
-
NIO server端
-
@author michael
-
@date 2019-03-28 11:17
*/
public class NIOServer {private int port = 8089;
private InetSocketAddress address = null;
private Selector selector = null;
/**
- 构造函数
*/
public NIOServer() throws Exception {
//设置IP,端口相关的属性
address = new InetSocketAddress(this.port);
//不管做什么,都要创建高速通道
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(address);
server.configureBlocking(false);
//大管家开始工作,开门营业
selector = Selector.open();
//Option的简称
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println(“服务器准备就绪,监听端口是:” + this.port);
}
public void listen() {
try {
//轮询
while (true) {
//有多少个人在服务大厅排队
int wait = this.selector.select();
if (wait == 0) {
continue;
}
Set keys = this.selector.selectedKeys();
Iterator i = keys.iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
//
process(key);
i.remove();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}public void process(SelectionKey key) throws Exception {
ByteBuffer buffer = ByteBuffer.allocate(1024);
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
int len = client.read(buffer);
if (len > 0) {
buffer.flip();
String content = new String(buffer.array(), 0, len);
System.out.println(content);
client.register(selector, SelectionKey.OP_WRITE);
}
buffer.clear();
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
client.write(buffer.wrap(“Hello Wold”.getBytes()));
client.close();
}
}public static void main(String[] args) throws Exception {
new NIOServer().listen();
}
}
client:
1.不管什么,先建一条高速公路先client =SocketChannel.open(inetSocketAddress);
2.往这条通道输出数据 client.write(charset.encode(name)); - 构造函数
server端:
1.首先,我们都要先建高速公路通道, ServerSocketChannel server = ServerSocketChannel.open();,需要绑定地址,指定最大连接数:server.bind(address(InetSocketAddress));设置为非阻塞的:server.configureBlocking(false);
2.Selector营业大厅开门营业了,到的client,他都会把他接进来
开门营业 selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
3.接着我们死循环,不断的看有没有人在排号等待,如果有的话,就要把排号的那一批人叫过来
if (wait == 0) {
continue;
}
Set keys = this.selector.selectedKeys();
接着遍历他们每一个人的排号码,根据排号码,每一个人就来到服务台,读到他们的信息
SelectionKey key = i.next();
process(key);
SocketChannel client = (SocketChannel) key.channel();
int len = client.read(buffer);
if (len > 0) {
buffer.flip();
String content = new String(buffer.array(), 0, len);
System.out.println(content);
client.register(selector, SelectionKey.OP_WRITE);
}
buffer.clear();
4.办完业务,就把他remove掉