NIO监听网络通信

NIO中比较重要的有:Buffer,ByteBuffer(要了解此类的postion、limit等)、chanel(SelectableChannel、ServerSocketChannel、SocketChanel)和Selector、SelectorKey,注意SelectorKey的种类ServerSocketChannel比SocketChanel多一个accept,其他三个为connect、read和write,因为ServerSocketChannel对应于旧IO中的ServerSocket,SocketChannel对应于旧IO中的Socket。DataProgramChannel与SocketChanel类似。


下面是一个Server和Client的例子,用来使用NIO进行通信。简单的说,就是创建好对象channel,接着在channel上注册好监听事件SelectorKey,然后调用channel的select()得到所有的Key的set,然后iterate该set根据具体的Key类型进行处理,基本上Key分为accept, read 和write,注意在指定方法处理key之前要将此key从interator中删除, accept就是server接受client的请求,read和write是读写byteBuffer。注意的是*Buffer有两个方法比较重要clear和flip,前者是清空,后者是紧实,因为*Buffer在创建的时候指定了大小,而每次读写实际上Buffer中的东东可能未满,就要通过flip来紧实一下喽,即在读Buffer中的数据之前都要进行一下flip, 如果这个buffer还要重用则就要进行clear


import java.io.IOException;
import java.net.InetAddress;
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.Date;
import java.util.Iterator;
import java.util.Set;
public class Server{
	private final Selector selector;
	private final ServerSocketChannel serverSocketChannel;
	
	public Server(int port) throws IOException{
		// 创建选择器
		selector = Selector.open();
		// 打开监听信道
		serverSocketChannel = ServerSocketChannel.open();
		InetSocketAddress adress = new InetSocketAddress(InetAddress.getLocalHost(),port);
		//与本地端口绑定
		serverSocketChannel.socket().bind(adress);
		// 设置为非阻塞模式
		serverSocketChannel.configureBlocking(false);
		// 注册选择器.并在注册过程中指出该信道可以进行Accept操作
		serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);		
	}
	public void start() {
		System.out.println("the server is started......");
		while (true) {
			try {
				int nKeys = selector.select();
				if (nKeys > 0){
					// selectedKeys()中包含了每个准备好某一I/O操作的信道的SelectionKey
					Set<SelectionKey> scSet = selector.selectedKeys();
					Iterator<SelectionKey> iter = scSet.iterator();
					while (iter.hasNext()) {
						SelectionKey key = (SelectionKey) iter.next();
						iter.remove();
						dispatch(key);					
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}
	}
	
	public void dispatch(SelectionKey key) {
		// 有客户端连接请求时
		//if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
		if (key.isAcceptable()) {
			try {
				System.out.println("Key is acceptable");
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				SocketChannel socket = (SocketChannel) ssc.accept();
				socket.configureBlocking(false);
				socket.register(selector, SelectionKey.OP_READ);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		// 从客户端读取数据
		//else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
		else if (key.isReadable()) {
			System.out.println("the key is readable");
			// new Thread(new ReadeHandler(key)).start();
			try {
				SocketChannel socket = (SocketChannel) key.channel();
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				int bytesRead = socket.read(buffer);
				if (bytesRead > 0) {
					buffer.flip();
					// 将字节转化为为UTF-16的字符串
					String receivedString = Charset.forName("UTF-16").newDecoder().decode(buffer).toString();
					// 控制台打印出来
					System.out.println("接收到来自"
							+ socket.socket()
									.getRemoteSocketAddress()
							+ "的信息:" + receivedString);
					// 准备发送的文本
					String sendString = "你好,客户端. @"
							+ new Date().toString() + ",已经收到你的信息:"
							+ receivedString;
					buffer = ByteBuffer.wrap(sendString
							.getBytes("UTF-16"));
					socket.write(buffer);
					// 设置为下一次读取或是写入做准备
					key.interestOps(SelectionKey.OP_READ);				
				}
			} catch (IOException e) {
				//客户端断开连接,所以从Selector中取消注册 
				key.cancel();
				if(key.channel() != null)
					try {
						key.channel().close();
						System.out.println("the client socket is closed!");
					} catch (IOException e1) {
						e1.printStackTrace();
					}
			}
		}
		// 客户端可写时
		else if (key.isWritable()) {
		    System.out.println("tHe key is writable");
		    //new Thread(new WriteHandler(key)).start();
		    //do something
		}
	}	
	
	public static void main(String[] args) throws IOException {
		Server server = new Server(9911);
		server.start();
	}
}


import java.io.IOException;
import java.net.InetAddress;
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.nio.charset.Charset;
public class Client {
	// 信道选择器
	private Selector selector;
	// 与服务器通信的信道
	SocketChannel socketChannel;
	
	public Client(int port)throws IOException{
		selector = Selector.open();
		socketChannel = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(),port));
	    socketChannel.configureBlocking(false);
	    socketChannel.register(selector, SelectionKey.OP_READ);
	    // 启动读取线程
	    new Thread(new ClientReadThread()).start();
	}
	//发送字符串到服务器
	public void sendMsg(String message) throws IOException{
		ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("UTF-16"));
	    socketChannel.write(writeBuffer);
	}
	
	public static void main(String[] args) throws IOException{
	    Client client = new Client(9911);	    
	    client.sendMsg("你好!Nio!");
	}
	
	class ClientReadThread implements Runnable{		
		public void run() {
		    try {
		      while (selector.select() > 0) {
		        // 遍历每个有可用IO操作Channel对应的SelectionKey
		        for (SelectionKey sk : selector.selectedKeys()) {		          
		          // 如果该SelectionKey对应的Channel中有可读的数据
		          if (sk.isReadable()) {
		            // 使用NIO读取Channel中的数据
		            SocketChannel sc = (SocketChannel) sk.channel();
		            ByteBuffer buffer = ByteBuffer.allocate(1024);
		            sc.read(buffer);
		            buffer.flip();		            
		            // 将字节转化为为UTF-16的字符串   
		            String receivedString = Charset.forName("UTF-16").newDecoder().decode(buffer).toString();		            
		            // 控制台打印出来
		            System.out.println("接收到来自服务器" + sc.socket().getRemoteSocketAddress() + "的信息:" + receivedString);
		            // 为下一次读取作准备
		            sk.interestOps(SelectionKey.OP_READ);
		          }
		          // 删除正在处理的SelectionKey
		          selector.selectedKeys().remove(sk);
		        }
		      }
		    } catch (IOException ex) {
		      ex.printStackTrace();
		    }   
		}
	}
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值