netty入门学习(1)-从NIO说起

学习netty之前,先来一个NIO的入门级示例(当然netty不仅仅对NIO进行了封装支持,对BIO同样也支持,本系统只针对NIO进行处理):

一:

服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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;


public class ChargenServer {
	public static int DEFAULT_PORT = 19;
	public static void main(String[] args){
		int port;
		try{
			port = Integer.parseInt(args[0]);
		}catch(Exception ex){
			port = DEFAULT_PORT;
		}
		
		System.out.println("Listening for connections on port " + port);
		
		byte[] rotation = new byte[95*2];
		for(byte i=' ';i<='~';i++){
			rotation[i-' '] = i;
			rotation[i+95-' '] = i;
		}
		
		ServerSocketChannel serverChannel;
		Selector selector;
		try{
			serverChannel = ServerSocketChannel.open();
			ServerSocket ss = serverChannel.socket();
			InetSocketAddress address = new InetSocketAddress(port);
			ss.bind(address);
			serverChannel.configureBlocking(false);
			selector = Selector.open();
			serverChannel.register(selector, SelectionKey.OP_ACCEPT);
		}catch(IOException ex){
			ex.printStackTrace();
			return;
		}
		while(true){
			try{
				selector.select();
			}catch(IOException ex){
				ex.printStackTrace();
				break;
			}
			Set readyKeys = selector.selectedKeys();
			Iterator iterator = readyKeys.iterator();
			while(iterator.hasNext()){
				SelectionKey key = (SelectionKey)iterator.next();
				iterator.remove();
				try{
					if(key.isAcceptable()){
						ServerSocketChannel server = (ServerSocketChannel)key.channel();
						SocketChannel client = server.accept();
						System.out.println("Accepted connection from " + client);
						client.configureBlocking(false);
						SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
						ByteBuffer buffer = ByteBuffer.allocate(74);
						buffer.put(rotation,0,72);
						buffer.put((byte)'\r');
						buffer.put((byte)'\n');
						buffer.flip();
						key2.attach(buffer);
					}else if(key.isWritable()){//测试此键的通道是否已准备好进行写入
						SocketChannel client = (SocketChannel)key.channel();
						ByteBuffer buffer = (ByteBuffer)key.attachment();
						if(!buffer.hasRemaining()){
							buffer.rewind();
							int first = buffer.get();
							buffer.rewind();
							int position = first - ' ' + 1;
							buffer.put(rotation,position,72);
							buffer.put((byte)'\r');
							buffer.put((byte)'\n');
							buffer.flip();
						}
						client.write(buffer);
					}
				}
					catch(IOException ex){
						key.cancel();
						try{
							key.channel().close();	
						}catch(IOException cex){}
					}
			}
		}		
	}
}


二:

客户端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;


public class ChargenClient {
	public static int DEFAULT_PORT = 19;
	public static void main(String[] args){
		try{
			SocketAddress address = new InetSocketAddress(DEFAULT_PORT);
			SocketChannel client = SocketChannel.open(address);
			ByteBuffer buffer=ByteBuffer.allocate(74);
			WritableByteChannel out = Channels.newChannel(System.out);
			while(client.read(buffer) != -1){
				buffer.flip();
				out.write(buffer);
				buffer.clear();
			}
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}
}

在这个示例中有几点需要注意:

(1)服务端的写事件什么时候触发?服务在对accept事件进行处理时,将写事件注册到注册集合,JVM会根据服务端的TCP缓冲区(这个是内核态的,区别于ByteBuffer等用户态缓冲区)是否可写入(未满)来决定是否将写事件加入触发集合,即触发写操作。注意:应根据业务场景来决定是否注销写事件,如果不注销,很容易造成CPU空转。

当然也可以将触发写操作的业务代码写到对accpet事件的处理中,如下。但是这样做有可能因TCP缓冲区已满,而引起写阻塞,违背了NIO的初忠。

	public static void main(String[] args){
		int port;
		try{
			port = Integer.parseInt(args[0]);
		}catch(Exception ex){
			port = DEFAULT_PORT;
		}
		
		System.out.println("Listening for connections on port " + port);
		
		byte[] rotation = new byte[95*2];
		for(byte i=' ';i<='~';i++){
			rotation[i-' '] = i;
			rotation[i+95-' '] = i;
		}
		
		ServerSocketChannel serverChannel;
		Selector selector;
		try{
			serverChannel = ServerSocketChannel.open();
			ServerSocket ss = serverChannel.socket();
			InetSocketAddress address = new InetSocketAddress(port);
			ss.bind(address);
			serverChannel.configureBlocking(false);
			selector = Selector.open();
			serverChannel.register(selector, SelectionKey.OP_ACCEPT);
		}catch(IOException ex){
			ex.printStackTrace();
			return;
		}
		while(true){
			try{
				selector.select();
			}catch(IOException ex){
				ex.printStackTrace();
				break;
			}
			Set readyKeys = selector.selectedKeys();
			Iterator iterator = readyKeys.iterator();
			while(iterator.hasNext()){
				SelectionKey key = (SelectionKey)iterator.next();
				iterator.remove();
				try{
					if(key.isAcceptable()){
						ServerSocketChannel server = (ServerSocketChannel)key.channel();
						SocketChannel client = server.accept();
						System.out.println("Accepted connection from " + client);
						client.configureBlocking(false);

						ByteBuffer buffer = ByteBuffer.allocate(74);
						buffer.put(rotation,0,72);
						buffer.put((byte)'\r');
						buffer.put((byte)'\n');
						buffer.flip();
						client.write(buffer);
					}
				}
					catch(IOException ex){
						key.cancel();
						try{
							key.channel().close();	
						}catch(IOException cex){}
					}
			}
		}
		

	}


(2)在NIO模型中,服务端一次写操作,有可能会导致客户端多次读操作。同样,服务端多次写操作,也可能只会触发客户端一次读操作。所以在具体的业务操作编程时,需要对此种情况进行处理。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值