IO之NIO

1、NIO工作方式

   用NIO方式处理IO使用多路复用器Selector来轮询每个通道Channel,当通道中有事件时就通知处理。比较重要的是弄懂Selector和Channel概念

2、服务端代码

package com.guosen.nio;

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 NioServer {
	/**
	 * 缓冲区大小
	 */
	private int BLOCK = 4096;
	/**
	 * Selector
	 */
	private Selector selector;
	 /**
	  * 发送数据缓冲区
	  */ 
	private ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
	 /**
	  * 接收数据缓冲区
	  */
	private ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);
	
	public NioServer(int port) throws Exception{
		/*
		 * 打开服务器套接字通道
		 */
		ServerSocketChannel ssc = ServerSocketChannel.open();
		/*
		 * 配置为非阻塞
		 */
		ssc.configureBlocking(false);
		
		/*
		 * 检索与通道相关的服务器套接字
		 */
		ServerSocket ss = ssc.socket();
		/*
		 * 将服务和地址绑定
		 */
		ss.bind(new InetSocketAddress("127.0.0.1", port));
		
		/*
		 * 通过open方法打开一个Selector
		 */
		selector = Selector.open();
		/*
		 * 注册到Selector等待连接
		 */
		ssc.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("server start on " + port);
	}
	
	private void listen() throws Exception{
		while (true) {
			selector.select();
			
			Set<SelectionKey> set = selector.selectedKeys();
			
			Iterator<SelectionKey> iterator = set.iterator();
			
			while (iterator.hasNext()) {
				SelectionKey selectionKey = iterator.next();
				iterator.remove();
				/**
				 * 处理事件
				 */
				try {
					handleKey(selectionKey);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void handleKey(SelectionKey selectionKey) throws Exception{
		ServerSocketChannel server = null;
		SocketChannel client = null;
		String receiveText = null;
		String sendText = null;
		int count = 0;
		
		/*
		 * 如果连接已经无效,则主动抛出异常结束该连接
		 */
		if (!selectionKey.isValid()) {
			client = (SocketChannel) selectionKey.channel();
			throw new Exception("客户已经主动关闭");
		}
		
		if (selectionKey.isAcceptable()) {
			/*
			 * 获取通道
			 */
			server = (ServerSocketChannel) selectionKey.channel();
			/*
			 * 从通道接收一个连接
			 */
			client = server.accept();
			/*
			 * 设置为非堵塞方式
			 */
			client.configureBlocking(false);
			/*
			 * 将通道注册为读操作
			 */
			client.register(selector, SelectionKey.OP_READ);
		} else if (selectionKey.isReadable()) {
			/*
			 * 获取到通道
			 */
			client = (SocketChannel) selectionKey.channel();
			/*
			 * 清空缓冲区
			 */
			receiveBuffer.clear();
			/*
			 * 从缓冲区读数据
			 */
			count = client.read(receiveBuffer);
			if (count > 0) {
				receiveText = new String(receiveBuffer.array(), 0, count);
				System.out.println("服务器接收到数据==>" + receiveText);
				/*
				 * 将通道注册为写操作
				 */
				client.register(selector, SelectionKey.OP_WRITE);
				
			}
		} else if (selectionKey.isWritable()) {
			/*
			 * 将缓冲区清空
			 * 将position设置为0
			 * 将limit设置为capital
			 */
			sendBuffer.clear();
			/*
			 * 获取到通道
			 */
			client = (SocketChannel) selectionKey.channel();
			
			sendText = "message is received on time[" + System.currentTimeMillis() + "]";
			
			/*
			 * 向缓冲区中放入数据
			 */
			sendBuffer.put(sendText.getBytes());
			/*
			 * 将limit置为position
			 * 将position置为0
			 */
			sendBuffer.flip();
			/*
			 * 将缓冲区的数据发送给客户端 
			 */
			client.write(sendBuffer);
			
			System.out.println("服务器已经向客户端发送数据==>" + sendText);
			/*
			 * 将通道注册为读操作
			 */
			client.register(selector, SelectionKey.OP_READ);
		}
	}
	
	
	public static void main(String[] args) throws Exception{
		NioServer ns = new NioServer(8080);
		ns.listen();
	}
}

3、客户端代码

package com.guosen.nio;

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.util.Iterator;
import java.util.Set;

public class NioClient {
	/**
	 * 缓冲区大小
	 */
	private static int BLOCK = 4096;
	/**
	 * 发送数据缓冲区
	 */
	private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
	/**
	 * 接收数据缓冲区
	 */
	private static ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);
	
	public static void main(String[] args) throws Exception{
		SocketChannel socketChannel = SocketChannel.open();
		socketChannel.configureBlocking(false);
		
		Selector selector = Selector.open();
		
		socketChannel.register(selector, SelectionKey.OP_CONNECT);
		socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
		
		Set<SelectionKey> selectKeys = null;
		SocketChannel client; 
		String receiveText;
		String sendText;
		int count = 0;
		while (true) {
			selector.select();
			
			selectKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterator = selectKeys.iterator();
			while (iterator.hasNext()) {
				SelectionKey key = iterator.next();
				
				if (key.isConnectable()) {
					client = (SocketChannel) key.channel();
					if (client.isConnectionPending()) {
						client.finishConnect();
						sendBuffer.clear();
						sendBuffer.put("Hello, Server".getBytes());
						sendBuffer.flip();
						client.write(sendBuffer);
					}
					client.register(selector, SelectionKey.OP_READ);
				} else if (key.isReadable()) {
					client = (SocketChannel) key.channel();
					receiveBuffer.clear();
					
					count = client.read(receiveBuffer);
					if (count > 0) {
						receiveText = new String(receiveBuffer.array(), 0, count);
						System.out.println("客户端接收到服务器的数据==>" + receiveText);
						client.register(selector, SelectionKey.OP_WRITE);
					}
				} else if (key.isWritable()) {
					sendBuffer.clear();
					
					client = (SocketChannel) key.channel();
					
					sendText = "message from cliet, time is[" + System.currentTimeMillis() + "]";
					sendBuffer.put(sendText.getBytes());
					sendBuffer.flip();
					client.write(sendBuffer);
					System.out.println("客户端向服务器端发送数据==>" + sendText);
					client.register(selector, SelectionKey.OP_READ);
				}
				selectKeys.clear();
			}
		}
		
	}

}

也可以用telnet 127.0.0.1 8080来测试服务端程序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值