一、Buffer缓冲区
package com.itbac.net.NIO.BIO;
import java.nio.ByteBuffer;
/**
* Buffer缓冲区
*/
public class BufferDemo {
public static void main(String[] args) {
//构建一个byte 字节缓冲区,容量是4
// allocate分配内存(在堆中)堆中内存要复制到堆外才能用于网络通信,因为堆中有GC管理
// allocateDirect 分配内存(在堆外),减少一次内存复制操作,提高性能,但是要自己回收内存
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
System.out.println(String.format("初始化:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
//写入3字节的数据
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
byteBuffer.put((byte) 3);
System.out.println(String.format("写入3字节后:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
System.out.println("开始读取——");
byteBuffer.flip();
System.out.println(String.format("切换成读模式:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
byte a = byteBuffer.get();
System.out.println("读第1个:"+a);
System.out.println(String.format("读取1字节后:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
byte b = byteBuffer.get();
System.out.println("读第2个:"+b);
System.out.println(String.format("读取2字节后:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
/**
* 继续写入3字节,此时读模式下,limit=3,position=2,继续写入只能写入一条数据,再多就会溢出。java.nio.BufferOverflowException
* clear()方法清除整个缓存去。 compact()方法仅清除已阅读的数据。==> 两个方法都会转为写入模式。
*/
byteBuffer.compact();
System.out.println(String.format("compact()后:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
byteBuffer.put((byte) 4);
byteBuffer.put((byte) 5);
byteBuffer.put((byte) 6);
System.out.println(String.format("最终的情况:capacity容量:%s,position位置:%s,limit限制:%s",
byteBuffer.capacity(),
byteBuffer.position(),
byteBuffer.limit()));
// byteBuffer.rewind(); 重置position为0
// byteBuffer.mark(); 标记position的位置
// byteBuffer.reset(); 重置position为上次 mark()标记的位置
}
}
二、Channel通道
客户端
package com.itbac.net.NIO.BIO;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
//客户端
public class NIOClient {
public static void main(String[] args) throws IOException {
//创建客户端通道
SocketChannel socketChannel = SocketChannel.open();
//设置非阻塞
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
while (!socketChannel.finishConnect()) {
//没连接上,则让出CPU ,一直等待
Thread.yield();
}
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
//发送内容
String msg = scanner.nextLine();
//包装要发出的数据
ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
while (byteBuffer.hasRemaining()) {
//如果Buffer缓冲区有剩余,循环写入通道中。
socketChannel.write(byteBuffer);
}
//读取响应
System.out.println("收到服务端响应:");
//开辟接收数据内存空间,堆内
ByteBuffer requestBuffer = ByteBuffer.allocate(1024);
//通道开启状态 & 从通道读数据,写入到 buffer缓存区
while (socketChannel.isOpen()&& socketChannel.read(requestBuffer) !=-1) {
//长连接情况下,需要手动判断数据有没有读取结束。
if (requestBuffer.position()==requestBuffer.limit()) {
//buffer缓冲区,写入模式切换成读取模式
requestBuffer.flip();
byte[] content = new byte[requestBuffer.limit()];
requestBuffer.get(content);
System.out.println(new String(content));
//清空已读,转成写模式
requestBuffer.compact();
}
}
//大于0 ,有写入的数据
if (requestBuffer.position()>0) {
//buffer缓冲区,写入模式切换成读取模式
requestBuffer.flip();
byte[] content = new byte[requestBuffer.limit()];
requestBuffer.get(content);
System.out.println(new String(content));
//清空已读,转成写模式
requestBuffer.compact();
}
//关闭键盘录入
scanner.close();
//关闭通道
socketChannel.close();
}
}
服务端
package com.itbac.net.NIO.BIO;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class NIOServer {
public static void main(String[] args) throws IOException {
//创建网络服务端
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//设置非阻塞IO
serverSocketChannel.configureBlocking(false);
//获取服务端套接字
ServerSocket serverSocket = serverSocketChannel.socket();
//绑定地址
serverSocket.bind(new InetSocketAddress("127.0.0.1",8080));
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept(); // 获取新tcp连接通道
// tcp请求 读取/响应
if (socketChannel != null) {
System.out.println("收到新连接 : " + socketChannel.getRemoteAddress());
socketChannel.configureBlocking(false); // 默认是阻塞的,一定要设置为非阻塞
try {
ByteBuffer requestBuffer = ByteBuffer.allocate(1024);
while (socketChannel.isOpen() && socketChannel.read(requestBuffer) != -1) {
// 长连接情况下,需要手动判断数据有没有读取结束 (此处做一个简单的判断: 超过0字节就认为请求结束了)
if (requestBuffer.position() > 0) {
break;
}
}
if(requestBuffer.position() == 0) {
continue; // 如果没数据了, 则不继续后面的处理
}
requestBuffer.flip();
byte[] content = new byte[requestBuffer.limit()];
requestBuffer.get(content);
System.out.println(new String(content));
System.out.println("收到数据,来自:"+ socketChannel.getRemoteAddress());
// 响应结果 200
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 11\r\n\r\n" +
"Hello World";
ByteBuffer buffer = ByteBuffer.wrap(response.getBytes());
while (buffer.hasRemaining()) {
socketChannel.write(buffer);// 非阻塞
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 用到了非阻塞的API, 在设计上,和BIO可以有很大的不同.继续改进
}
}