Netty-NIO

文章目录


一、NIO-Selector

1.处理accept

java 复制代码
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
//SelectionKey就是将来事件发生后,通过它可以知道事件和哪个channel的事件
//四个事件:
//accept 会在有连接请求时触发
//connect 是客户端,连接建立后触发
//read 可读事件
//write 可写事件
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){
	//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行
	selector.select();
	//4.处理事件,selectedKeys内部包含了所有发生的事件
	Iterator<SelectionKey> iter = selector.selectedKeys.iterator();
	while(iter.next()){
		SelectionKey key = iter.next();
		ServerSocketChannel channel = (ServerSocketChannel)key.channel();
		SocketChannel sc = channel.accept();
	}
}

2.cancel

java 复制代码
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){
	//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行
	//select在事件未处理时,它不会阻塞,事件发生后要么处理,要么取消,不能置之不理
	selector.select();
	//4.处理事件,selectedKeys内部包含了所有发生的事件
	Iterator<SelectionKey> iter = selector.selectedKeys.iterator();
	while(iter.next()){
		SelectionKey key = iter.next();
		key.cancel();
	}
}

3.处理read

用完key必须要remove

java 复制代码
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){
	//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行
	selector.select();
	//4.处理事件,selectedKeys内部包含了所有发生的事件
	//selector会在发生事件后,向集合中加入key,但不会删除
	Iterator<SelectionKey> iter = selector.selectedKeys.iterator();
	while(iter.next()){
		SelectionKey key = iter.next();
		//处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题
		iter.remove();
		//5.区分事件类型
		if(key.isAcceptable()){ //如果是accept
			ServerSocketChannel channel = (ServerSocketChannel)key.channel();
			SocketChannel sc = channel.accept();
			sc.configureBlocking(false);
			SelectionKey sckey = sc.register(selector, 0, null);
			scKey.interestOps(SelectionKey.OP_READ);
		}elseif(key.isReadable()){
			//拿到触发事件的channel
			ServerSocketChannel channel = (ServerSocketChannel)key.channel();
			ByteBuffer buffer = ByteBuffer.allocate(16);
			channel.read(buffer);
			buffer.flip();
			debugRead(buffer);
		}
	}
}

4.处理客户端断开

java 复制代码
//1.创建selector,管理多个channel
Selector selector = Selector.open(); 
ByteBuffer buffer = ByteBuffer.allocate(16); 
ServerSocketChannel ssc = ServerSocketChannel.open(); 
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null); sscKey.interestOps(SelectionKey.OP_ACCEPT); 
ssc.bind(new InetSocketAddress(8080)); 
while(true){ 
	//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行 
	selector.select(); 
	//4.处理事件,selectedKeys内部包含了所有发生的事件 
	//selector会在发生事件后,向集合中加入key,但不会删除 
	Iterator<SelectionKey> iter = selector.selectedKeys.iterator(); 
	while(iter.next()){ 		
		SelectionKey key = iter.next(); 
		//处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题 
		iter.remove(); 
		//5.区分事件类型 
		if(key.isAcceptable()){ 
			//如果是accept 
			ServerSocketChannel channel = (ServerSocketChannel)key.channel(); 
			SocketChannel sc = channel.accept();
		 	sc.configureBlocking(false); 
		 	SelectionKey sckey = sc.register(selector, 0, null); 
		 	scKey.interestOps(SelectionKey.OP_READ); 
		 }elseif(key.isReadable()){ 
		 	try{ 
			 	//拿到触发事件的channel 
			 	ServerSocketChannel channel = (ServerSocketChannel)key.channel(); 
			 	ByteBuffer buffer = ByteBuffer.allocate(16); 
			 	int read = channel.read(buffer);//如果是正常断开,read的方法的返回值是-1 
			 	if(read == -1){ 
			 		key.cancel(); 
			 	}else{ 
			 	buffer.flip(); 
			 	debugRead(buffer); 
		 		} 
		 	}catch(IOException e){ 
		 		e.printStackTrace();
		 	 	//因为客户端断开了,因此需要将key取消(从selector 的keys集合中真正删除key) 
		 	 	key.cancel();
			}
		}
	}
}

5. 处理消息的边界

  1. 固定消息长度,数据包大小一样,服务器按预定长度读取,缺点是浪费带宽
  2. 按分隔符拆分,缺点是效率低
  3. TLV格式,Type类型,Length长度,Value数据,可以方便获取消息大小,分配合适的buffer,缺点是buffer需要提前分配,如果内容过大,影响server吞吐量
    • Http1.1是TLV格式
    • Http2.0是LTV格式
java 复制代码
private static void split(ByteBuffer source){
	source.flip();
	for(int i = 0; i < source.limit(); i++){
		//找到一条完整消息
		if(source.get(i) == '\n'){
			int length = i + 1 -source.position();
			//把这条完整消息存入新的ByteBuffer
			ByteBuffer target = ByteBuffer.allocate(length);
			//从source读,向target写
			for(int j = 0; j < length; j++){
				target.put(source.get());
			}
			debugAll(target);
		}
	}
	source.compact();
}

public static void main(){
	//1.创建selector,管理多个channel
	Selector selector = Selector.open(); 
	ByteBuffer buffer = ByteBuffer.allocate(16); 
	ServerSocketChannel ssc = ServerSocketChannel.open(); 
	ssc.configureBlocking(false);
	//2.建立selector和channel的联系(注册)
	SelectionKey sscKey = ssc.register(selector, 0, null); 		
	sscKey.interestOps(SelectionKey.OP_ACCEPT); 
	ssc.bind(new InetSocketAddress(8080)); 
	while(true){ 
		//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行 
		selector.select(); 
		//4.处理事件,selectedKeys内部包含了所有发生的事件 
		//selector会在发生事件后,向集合中加入key,但不会删除 
		Iterator<SelectionKey> iter = selector.selectedKeys.iterator(); 
		while(iter.next()){ 		
			SelectionKey key = iter.next(); 
			//处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题 
			iter.remove(); 
			//5.区分事件类型 
			if(key.isAcceptable()){ 
				//如果是accept 
				ServerSocketChannel channel = (ServerSocketChannel)key.channel(); 
				SocketChannel sc = channel.accept();
			 	sc.configureBlocking(false); 
			 	ByteBuffer buffer = ByteBuffer.allocate(16); //attachment附件
			 	//将一个byteBuffer作为附件关联到selectionKey上
			 	SelectionKey sckey = sc.register(selector, 0, buffer); 
			 	scKey.interestOps(SelectionKey.OP_READ); 
			 }elseif(key.isReadable()){ 
			 	try{ 
				 	//拿到触发事件的channel 
				 	ServerSocketChannel channel = (ServerSocketChannel)key.channel(); 
				 	//获取selectionKey上关联的附件
				 	ByteBuffer buffer = (ByteBuffer)key.attatchment();
				 	int read = channel.read(buffer);//如果是正常断开,read的方法的返回值是-1 
				 	if(read == -1){ 
				 		key.cancel(); 
				 	}else{ 
				 		split(buffer);
				 		if(buffer.position() == buffer.limit()){
				 			//扩容
				 			ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);
				 			buffer.flip();
				 			newBuffer.put(buffer);//复制
				 			key.attach(newbuffer);//替换掉key上原有的buffer
				 		}
			 		} 
			 	}catch(IOException e){ 
			 		e.printStackTrace();
			 	 	//因为客户端断开了,因此需要将key取消(从selector 的keys集合中真正删除key) 
			 	 	key.cancel();
				}
			}
		}
	}
}

6. 写入内容过多的问题

java 复制代码
//服务器
public static void main(){
	ServerSocketChannel ssc = ServerSocketChannrl.open();
	ssc.configureBlocking(false);
	Selector selector = Selector.open();
	ssc.register(selector, SelectionKey.OP_ACCEPT);
	ssc.bind(new InetSocketAddress(8080));
	while(trye){
		selector.select();
		Iterator<SelectionKey> iter = selector.selectedKeys.iterator();
		while(iter.hasNext()){
			SelectionKey key = iter.next();
			iter.remove();
			if(key.isAcceptable()){
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				//1.向客户端发送大量数据
				StringBuilder sb = new StringBuilder();
				for(int i = 0; i < 3000000; i++){
					sb.append("a");
				}
				BytrBuffer buffer = Charset.defaultCharset().encode(sb.toString());
				//不符合非阻塞模式
				while(buffer.hasRemaining()){
					//2.返回值代表实际写入的字节数
					//不能一次性写完
					//write == 0 缓冲区满,写不了
					int write = sc.write(buffer);
					System.out.println(write):
				}
			}
		}
	}
}

//客户端
public static void main(){
	SocketChannel sc = SocketChannel.open();
	sc.connect(new InetSocketAddress("localhost",8080));
	//3.接收数据
	int count = 0;
	while(true){
		ByteBuffer buffer = ByteBuffer.allocate(1024*1024);
		count += sc.read(buffer);
		System.out.println(count);
		buffer.clear();
	}
}

7. 处理可写事件

java 复制代码
//服务器
public static void main(){
	ServerSocketChannel ssc = ServerSocketChannrl.open();
	ssc.configureBlocking(false);
	Selector selector = Selector.open();
	ssc.register(selector, SelectionKey.OP_ACCEPT);
	ssc.bind(new InetSocketAddress(8080));
	while(trye){
		selector.select();
		Iterator<SelectionKey> iter = selector.selectedKeys.iterator();
		while(iter.hasNext()){
			SelectionKey key = iter.next();
			iter.remove();
			if(key.isAcceptable()){
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				SelectionKey sckey = sc.register(selector, 0, null);
				sckey.interestOps(SelectionKey.OP_READ);
				//1.向客户端发送大量数据
				StringBuilder sb = new StringBuilder();
				for(int i = 0; i < 3000000; i++){
					sb.append("a");
				}
				BytrBuffer buffer = Charset.defaultCharset().encode(sb.toString());
				//2.返回值代表实际写入的字节数
				//不能一次性写完
				//先写一次
				int write = sc.write(buffer);
				System.out.println(write):
				//3.判断是否有剩余内容
				while(buffer.hasRemaining()){
					//4.关注可写事件
					sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);
					//sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE);
					//5.把未写完的数据挂到sckey上
					sckey.attach(buffer);
				}
			}elseif(key.isWritable())[
				ByteBuffer buffer = (ByteBuffer) key.attachment();
				SocketChannel sc = (SocketChannel)key.channel();
				int write = sc.write(buffer);
				System.out.println(write):
				//6.清理操作,内存释放
				if(!buffer.haeRemaining()){
					key.attach(null);//需要清除buffer
					key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);//不需关注可写事件
				}
			}
		}
	}
}
相关推荐
医只鸡腿子14 分钟前
3.2/Q2,Charls最新文章解读
数据库·数据挖掘·数据分析·深度优先·数据库开发
bang___bang_32 分钟前
PostgreSQL内幕剖析——结构与架构
数据库·postgresql
keke1037 分钟前
Java【14_2】接口(Comparable和Comparator)、内部类
java·开发语言
CN.LG1 小时前
Java 乘号来重复字符串的功能
java·开发语言
萌新下岸多多关照1 小时前
Java中synchronized 关键字
java·开发语言
中国lanwp1 小时前
使用Maven部署WebLogic应用
java·maven
开开心心就好1 小时前
Word图片格式调整与转换工具
java·javascript·spring·eclipse·pdf·word·excel
龙俊亨1 小时前
达梦数据库查看各数据库表内容
数据库
CGG922 小时前
【单例模式】
android·java·单例模式
苦学编程的谢2 小时前
多线程代码案例-1 单例模式
java·开发语言·单例模式