BIO: Java 1.4 以前只有之中方式。
bio:阻塞式IO, 一个 socker 连接占用一个 线程。如果 IO 阻塞,会在传输速度限制,这个线程也会一直等待在这里,等待从socker 的 IO 流 中读写数据。
Java 基于 socker 的 连接方式都是 BIO,都是阻塞式的IO。
TCP:
server:
cilent:
原理图:
UDP:
server:
client:
NIO: Java 1.4 以后支持的IO方式
NIO: 非阻塞式的IO, NIO 不再是 传统socker 方式。 一个 IO请求过来,会有一个线程,来处理这个IO请求,但是如果这个IO阻塞,那么这个线程会被处理别的 IO请求的 的事情。如果阻塞的IO的操作完成(完成一个块数据写入缓冲区),那么就会有一个线程分配过来处理这部分数据。
selecter: NIO 不再是 传统socke的概念。 socker 一个 连接,就固定一个 线程来处理,并且这个线程值服务这个 socker。但是 NIO 改变了这种IO 模型, selecter 里面跑着一个独立的线程,这个线程管理 一些渠道( channel , 有点 类似 socker ),如果 某个
渠道的 数据准备好了,那么 selecter 就会分配一个线程来 读写 这个channet 的数据。
buffer: 前面 一直强调 当数据准备好了,什么样才叫数据准备好了? socker 方式的 io 模式是一个 字节 一个字节读写的。 NIO 是通过 一块一块读写的( 一块就是 多个字节 ),但是这个这个 一块数据是 需要 一个临时存放区域的,这就是buffer 。当一个 块 数据满了以后,就分配 线程 来执行这个请求。
channel: 相当于以前的 流的 概念,可以 李杰成一个管道。BIO 一个连接一个线程,并且在socker结束以前一直占用着这个线程。NIO 是一个 也是一个连接一个线程。但是这个线程不会一直 等待这个 连接的IO操作。
例子代码:
server:
package comcxygg.test.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.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class NioServer {
public static void main(String[] args) throws Exception {
/**
* 开启一个服务端
* 设置为非阻塞
* 绑定端口号
*/
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8080));
System.out.println("serverSocketChannel:" + serverSocketChannel.hashCode() );
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//UUID->客户端连接
Map<String,SocketChannel> clientMap = new HashMap<>();
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
selectionKeys.forEach(selectionKey -> {
try {
if (selectionKey.isAcceptable()) {
/**
* 服务端接收到连接
* 保存接收到的客户端连接
*/
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
System.out.println("server:" + server.hashCode() );
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
String key = UUID.randomUUID().toString();
clientMap.put(key,socketChannel);
System.out.println(socketChannel.getRemoteAddress()+"连接上了服务器");
} else if (selectionKey.isReadable()) {
/**
* 读取客户端消息
* 转发到所有客户端
*/
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
System.out.println( "服务器端socketChannel:" + socketChannel.hashCode());
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = socketChannel.read(buffer);
if (len > 0) {
buffer.flip();
Charset charset = Charset.forName("UTF-8");
String receiveMsg = String.valueOf(charset.decode(buffer).array());
String key = null;
for (Map.Entry<String,SocketChannel> entry : clientMap.entrySet()) {
if (entry.getValue() == socketChannel) {
key = entry.getKey();
break;
}
}
String sendMsg = key + ":" + receiveMsg;
System.out.println(sendMsg);
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
writeBuffer.put(sendMsg.getBytes());
writeBuffer.flip();
clientMap.get(key).write( writeBuffer );
/* for (Map.Entry<String,SocketChannel> entry : clientMap.entrySet()) {
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
writeBuffer.put(sendMsg.getBytes());
writeBuffer.flip();
entry.getValue().write(writeBuffer);
}*/
}
}catch (Exception e) {
e.printStackTrace();//java.io.IOException: 远程主机强迫关闭了一个现有的连接。
socketChannel.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
});
selectionKeys.clear();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
client:
package comcxygg.test.nio;
import java.io.BufferedReader;
import java.io.InputStreamReader;
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.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NioClient {
public static void main(String[] args) throws Exception {
/**
* 开启一个客户端
* 设置为非阻塞
* 连接到服务器
*/
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost",8080));
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
System.out.println( "socketChannel:" + socketChannel.hashCode() );
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys) {
if (selectionKey.isConnectable()) {
/**
* 客户端已连接
* 开启一个线程监听控制台输入
*/
SocketChannel client = (SocketChannel) selectionKey.channel();
System.out.println( "client1:" + client.hashCode() );
if (client.isConnectionPending()) {
client.finishConnect();
}
client.register(selector,SelectionKey.OP_READ);
ExecutorService executor = Executors.newSingleThreadExecutor();
System.out.println(socketChannel.getLocalAddress()+"连上了服务器");
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
executor.submit(()->{
try {
while (true) {
writeBuffer.clear();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine();
writeBuffer.put(line.getBytes());
writeBuffer.flip();
client.write(writeBuffer);
}
}catch (Exception e) {
e.printStackTrace();
}
});
} else if (selectionKey.isReadable()) {
/**
* 打印服务端消息
*/
SocketChannel client = (SocketChannel) selectionKey.channel();
System.out.println( "client2:" + client.hashCode() );
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int len = client.read(readBuffer);
System.out.println(new String(readBuffer.array(),0,len));
}
}
selectionKeys.clear();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
BIO 图:
NIO 的 UDP 协议使用:
别的基本一样 只是 渠道使用的 DatagramChannel 。