Nio入门实现简单的服务端和客户端通信

Java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。目前很多通信都是基于Nio来实现。下面分享一个入门的案例。

服务端的代码如下:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
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.Iterator;
import java.util.Set;

public class NioServer {

	public static void main(String[] args) throws IOException {
		NioServer nioServer = new NioServer();
		nioServer.start();
	}
	
	
	/**
	 * 启动服务器
	 * @throws IOException 
	 */
	public void start() throws IOException {
		
		//1创建一个Selector
		Selector selector = Selector.open();
		
		//2通过ServerSocketChannel创建channel通道
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		
		//3为channel通道绑定端口
		serverSocketChannel.bind(new InetSocketAddress(8010));
		
		//4设置channel为非阻塞模式
		serverSocketChannel.configureBlocking(false);
		
		//5讲channel注册到selector上,监听链接事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("服务器启动成功...");
		
		//6循环,等待新进入的链接
		while(true){
			//是一个阻塞方法,只有监听事件就绪了才会返回,
			/**
			 * TODO 获取可用的channel数量
			 */
			int readyChannels = selector.select();
			
			if(readyChannels == 0) {
				continue;
			}
			
			/**
			 * 获取可用Channel的集合
			 */
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterable = selectionKeys.iterator();
			while(iterable.hasNext()) {
				/**
				 * selectionKey实例
				 */
				SelectionKey selectionKey = iterable.next();
				
				/**
				 * 移除当前的selectionKey
				 */
				iterable.remove();
				
				
				//7根据就绪状态调用对应方法处理业务逻辑
				/**
				 * 如果是接入事件
				 */
				if(selectionKey.isAcceptable()) {
					acceptHandler(serverSocketChannel,selector);
				}
				/**
				 * 如果是可读事件
				 */
				if(selectionKey.isReadable()) {
					readHandler(selectionKey, selector);
				}
			}
		}
	}
	
	
	/**
	 * 接入事件处理
	 * @throws IOException 
	 */
	private void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {
		/**
		 * 如果是接入事件,创建socketChannel
		 */
		SocketChannel socketChannel = serverSocketChannel.accept();
		
		/**
		 * 将socketChannel设置为非阻塞工作模式
		 */
		socketChannel.configureBlocking(false);
		
		/**
		 * 讲channel注册到selector上,监听可读事件
		 */
		socketChannel.register(selector, SelectionKey.OP_READ);
		
		/**
		 * 回复客户端提示信息
		 */
		socketChannel.write(Charset.forName("UTF-8").encode("您连接成功了"));
	}
	
	
	/**
	 * 可读事件处理
	 * @throws IOException 
	 */
	private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
		
		/**
		 * 要从selectionKey中获取到已经就绪的channel
		 */
		SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
		
		/**
		 * 创建buffer
		 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		
		/**
		 * 将channel再次注册到selector上,监听他的可读事件
		 */
		String request = "";
		while(socketChannel.read(buffer) > 1) {
			/**
			 * 切换为读模式
			 */
			buffer.flip();
			
			/**
			 * 读取buffer中的内容
			 */
			request += Charset.forName("UTF-8").decode(buffer);
			
		}
		/**
		 * 将客户端发送的请求信息,广播给所有其他客户端
		 */
		broadCast(selector,socketChannel,request);
		
		socketChannel.register(selector, SelectionKey.OP_READ);

		if(request.length() > 0) {
			System.out.println(request);
		}
		
		
	}
	
	/**
	 * 广播
	 * @throws IOException 
	 */
	private void broadCast(Selector selector,SocketChannel sourceChannel,String request) throws IOException {
		/**
		 * 获取所有已经接入的客户端channel
		 */
		Set<SelectionKey> selectionKeys = selector.keys();
		Iterator<SelectionKey> it = selectionKeys.iterator();
		
		/**
		 * 循环向所有channel发送数据
		 */
		while(it.hasNext()) {
			SelectionKey selectionKey = it.next();
			Channel channel = selectionKey.channel();
			
			//剔除发消息的客户端
			if(channel instanceof SocketChannel && channel != sourceChannel) {
				((SocketChannel)channel).write(Charset.forName("UTF-8").encode(request));
			}
		}
	}
}

客户端的代码如下:

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

public class NioClient {

	public static void main(String[] args) throws IOException {
		
		/**
		 * 连接服务器
		 */
		SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost",8010));
		System.out.println("客户端启动成功...");
		
		/**
		 * 接收服务器端的响应数据
		 * 新开一个线程
		 */
		Selector selector = Selector.open();
		socketChannel.configureBlocking(false);
		socketChannel.register(selector,SelectionKey.OP_READ);
		new Thread(new NioClientHandler(selector)).start();
		
		
		/**
		 * 向服务端发送数据
		 */
		Scanner scanner = new Scanner(System.in);
		while(scanner.hasNext()) {
			String request = scanner.nextLine();
			if(request != null && request.length() > 0) {
				socketChannel.write(Charset.forName("UTF-8").encode(request));
			}
		}
	}
}


class NioClientHandler implements Runnable{

	private Selector selector;
	
	public NioClientHandler(Selector selector) {
		this.selector = selector;
	}

	
	
	/**
	 * 可读事件处理
	 * @throws IOException 
	 */
	private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
		
		/**
		 * 要从selectionKey中获取到已经就绪的channel
		 */
		SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
		
		/**
		 * 创建buffer
		 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		
		/**
		 * 将channel再次注册到selector上,监听他的可读事件
		 */
		String request = "";
		while(socketChannel.read(buffer) > 1) {
			/**
			 * 切换为读模式
			 */
			buffer.flip();
			
			/**
			 * 读取buffer中的内容
			 */
			request += Charset.forName("UTF-8").decode(buffer);
		}
		/**
		 * 将服务器端的信息打印出来
		 */
		socketChannel.register(selector, SelectionKey.OP_READ);

		if(request.length() > 0) {
			System.out.println(request);
		}
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		while(true){
			//是一个阻塞方法,只有监听事件就绪了才会返回,
			/**
			 * TODO 获取可用的channel数量
			 */
			int readyChannels = 0;
			try {
				readyChannels = selector.select();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			
			if(readyChannels == 0) {
				continue;
			}
			
			/**
			 * 获取可用Channel的集合
			 */
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterable = selectionKeys.iterator();
			while(iterable.hasNext()) {
				/**
				 * selectionKey实例
				 */
				SelectionKey selectionKey = iterable.next();
				
				/**
				 * 移除当前的selectionKey
				 */
				iterable.remove();
				
				/**
				 * 如果是可读事件
				 */
				if(selectionKey.isReadable()) {
					try {
						readHandler(selectionKey, selector);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
}

有问题可以在下面评论,技术问题可以私聊我哦。

发布了106 篇原创文章 · 获赞 101 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/qq_24434671/article/details/97532979