非阻塞的NIO
1缓冲区
客户端
public class NIOClient {
public static void main(String[] args) throws IOException {
//1.创建通道对象并打开
SocketChannel socketChannel = SocketChannel.open();
//2.指定IP和端口号
socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));
//3.写出数据
ByteBuffer byteBuffer = ByteBuffer.wrap("一点寒芒先到".getBytes());
socketChannel.write(byteBuffer);
//4.关流
socketChannel.close();
}
}
服务端
public class NIOServer {
public static void main(String[] args) throws IOException {
// 1.创建打开服务端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2.绑定对应的端口号
serverSocketChannel.bind(new InetSocketAddress(10000));
// 3.通道默认是阻塞的,需要设置为非阻塞
//如果传递true 表示通道设置为阻塞通道...默认值
//如果传递false 表示通道设置为非阻塞通道
serverSocketChannel.configureBlocking(false);
// 4.此时没有门卫大爷,所以需要经常看一下有没有连接发过来没?
while (true) {
// 5.如果有客户端来连接了,则在服务端通道内部,再创建一个客户端通道
//设置了通道为非阻塞
//所以在调用方法的时候,如果有客户端来连接,那么会创建一个SocketChannel对象
//如果在调用方法的时候,没有客户端来连接,那么他会返回一个null
SocketChannel socketChannel = serverSocketChannel.accept();
//System.out.println(socketChannel);
if(socketChannel != null){
// 6.客户端将缓冲区通过通道传递给服务端,就到了这个延伸通道socketChannel里面
// 7.服务端创建一个空的缓冲区装数据并输出
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//获取传递过来的数据,并把他们放到byteBuffer缓冲区中.
//返回值:
//正数: 表示本次读到的有效字节个数.
//0 : 表示本次没有读到
//-1 : 表示读完了
int len = socketChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array(),0,len));
//8.关闭
socketChannel.close();
}
}
}
}
2通道
客户端
public class Clinet {
public static void main(String[] args) throws IOException {
// 1.创建打开通道
SocketChannel socketChannel = SocketChannel.open();
// 2.指定IP和端口号
socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));
// 3.写出数据
ByteBuffer byteBuffer1 = ByteBuffer.wrap("吃俺老孙一棒棒".getBytes());
socketChannel.write(byteBuffer1);
// 写入结束标记
socketChannel.shutdownOutput();
System.out.println("数据已经写给服务器");
// 4.读取服务器写回的数据
ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
int len;
while((len = socketChannel.read(byteBuffer2)) != -1){
byteBuffer2.flip();
System.out.println(new String(byteBuffer2.array(),0,len));
byteBuffer2.clear();
}
// 5.释放资源
socketChannel.close();
}
}
服务端
public class Sever {
public static void main(String[] args) throws IOException {
// 1,打开一个服务端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2,绑定对应的端口号
serverSocketChannel.bind(new InetSocketAddress(10000));
// 3,通道默认是阻塞的,需要设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 4,此时没有门卫大爷,所以需要经常看一下有没有连接发过来没?
while(true){
// 5,如果有客户端来连接了,则在服务端通道内部,再创建一个客户端通道,相当于是客户端通道的延伸
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel != null){
System.out.println("此时有客户端来连接了");
// 6,获取客户端传递过来的数据,并把数据放在byteBuffer1这个缓冲区中
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
//socketChannel.read(byteBuffer1);
int len;
//针对于缓冲区来讲
//如果 从添加数据 ----> 获取数据 flip
//如果 从获取数据 ----> 添加数据 clear
while((len = socketChannel.read(byteBuffer1)) != -1){
byteBuffer1.flip();
System.out.println(new String(byteBuffer1.array(),0,len));
byteBuffer1.clear();
}
System.out.println("接收数据完毕,准备开始往客户端回写数据");
// 7,给客户端回写数据
ByteBuffer byteBuffer2 = ByteBuffer.wrap("哎哟,真疼啊!!!".getBytes());
socketChannel.write(byteBuffer2);
// 8,释放资源
socketChannel.close();
}
}
}
}
3选择器
客户端
public static void main(String[] args) throws IOException {
//创建并打开通道
SocketChannel socketChannel = SocketChannel.open();
//指定ip和端口
socketChannel.connect(new InetSocketAddress("192.168.1.39",9999));
//写出数据
ByteBuffer byteBuffer1 = ByteBuffer.wrap(("你笑一次,我就可以高兴好几天;" +
"可看你哭一次,我就难过了好几年。").getBytes());
socketChannel.write(byteBuffer1);
System.out.println("数据已经写给服务器");
//存入字节数组
ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
int len;
while((len = socketChannel.read(byteBuffer2)) != -1){
System.out.println("客户端接收回写数据");
byteBuffer2.flip();
System.out.println(new String(byteBuffer2.array(),0,len));
byteBuffer2.clear();
}
//关闭
socketChannel.close();
}
服务端
public static void main(String[] args) throws Exception {
//1:建立服务器 的通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
//设置非阻塞连接
serverSocketChannel.configureBlocking(false);
//2:为了同同时处理多个客户端的连接
// 加上选择器
// 1:获得一个独立的选择器
Selector selector = Selector.open();
// 2:将当前的服务器 通道 注册上该选择器,并设置该通道的状态
// 状态有三种
// 可接受
// 可读取数据
// 可写出数据
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//正式开始 通过选择器 操作每一个客户端的数据输入
// 所有的数据输入和输出 通过选择器
while (true) {
//:1获得选择器中 的处理的 客户端 个数
int count = selector.select();
// 只有当前有客户才处理
if (count != 0) {
// SelectionKey ----> 就是一个客户端连接后 , 选择器给他分配一个 令牌 ,该令牌描述 该通道的状态
Set<SelectionKey> keys = selector.selectedKeys();
// 循环使用每一个令牌 , 其实就是循环操作每个 客户端通道
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 获得 当前令牌的状态 , 根据不同的状态 执行不同的代码
// 如果是第一次进入 去选择器登记和注册
if (key.isAcceptable()) { // 一个客户端 最先是在次 先执行,后续才能 读或者写
// 获得服务端通道
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 通过服务端 获得一个连接的客户端通道
SocketChannel client = server.accept();
//将客户端的通道设置为非阻塞
client.configureBlocking(false);
//修改当前选择器中 令牌的状态
// 如果填写是 SelectionKey.OP_READ 那么此客户端下一次进来时就是 进入 else if(key.isReadable())
// 如果填写是 SelectionKey.OP_WRITE 那么此客户端下一次进来时就是 进入 else if(key.isWritable())
client.register(selector, SelectionKey.OP_READ);
// 如果是读取数据的状态
} else if (key.isReadable()) {
//需要读取 客户端中的数据
SocketChannel client = (SocketChannel) key.channel();
//缓冲区获得数据
ByteBuffer buffer = ByteBuffer.allocate(8192);
while (true) {
buffer.clear();
//获得通道中的数据 写入缓冲中
int len = client.read(buffer);
if (len == -1) {
//说明 客户端发送全部结束
// 如果我想告诉 客户端 我接受了全部数据 --->我就是想给客户端发送一个消息
// 将当前客户端的状态修改为 可写
client.register(selector, SelectionKey.OP_WRITE);
break;
} else if (len == 0) {
// 通道连接是正常 ,但是数据不来了
break;
} else {
buffer.flip();
//将数据继续解析 ---> 我们先默认发送的字符串
String msg = new String(buffer.array(), 0, len);
String ip = client.getRemoteAddress().toString();
System.out.println(ip + " ---> " + msg);
}
}
// 如果是写出数据的状态
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.wrap("服务端接受数据完毕 ,你可以正常退出了".getBytes());
client.write(buffer);
// client.close();
// //可以将次通道关闭
// iterator.remove();
}
}
}
}
}