JAVA网络编程之基于NIO异步非阻塞服务器端的简单实现

Java NIO(New IO)是非阻塞的IO,所以NIO也被当成是non-blocking IO的简称,在jdk1.4以后的版本中提供支持。标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

Java NIO提供了与标准IO不同的IO工作方式:

Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。

Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

服务器采用非阻塞的NIO方式,还是传统的多线程方式?目前还没有证据表明谁优谁劣。下面的基于NIO非阻塞服务器端的简单实现仅供学习之用,工作环境请勿模仿,否则后果自负哦。

服务器端实现

package com.xieyincai.net;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioNonBlockingServer {

	private final static int PORT = 8000;
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		// 服务器通道
		ServerSocketChannel serverSocketChannel = null;
		// 选择器
		Selector selector = null;
		
		try {
			serverSocketChannel = ServerSocketChannel.open();
			serverSocketChannel.bind(new InetSocketAddress(PORT));
			// 配置为非阻塞模式,默认为阻塞模式。
			serverSocketChannel.configureBlocking(false);
			// 打开一个Selector对象
			selector = Selector.open();	
			// 向给定的选择器selector注册此通道
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		while(true) {
			try {
				int n = selector.select();
				// 选择器还无事可做,继续。。。
				if(n == 0) continue;				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterator = selectionKeys.iterator();
			
			while (iterator.hasNext()) {
				SelectionKey key = iterator.next();
				// 已经取出key,从迭代器中把当前key删除。
				iterator.remove();
				
				try {
					if (key.isAcceptable()) {
						ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
						SocketChannel clientChannel = serverChannel.accept();
						System.out.println("CLIENT:\t" + clientChannel.socket().getInetAddress() + " : " + clientChannel.socket().getPort());
						// 设置为非阻塞模式
						clientChannel.configureBlocking(false);
						// 注册到selector选择器上
						clientChannel.register(selector, SelectionKey.OP_READ);	
					}
					if(key.isReadable()) {
						SocketChannel clientChannel = (SocketChannel) key.channel();
						ByteBuffer buffer = ByteBuffer.allocate(4096);
						// 获取客户端发过来的数据
						clientChannel.read(buffer);	

						String content = new String(buffer.array(), "UTF-8");
						// 打印获取的客户端数据
						System.out.println(content);
						// 数据前面加“ECHO:\t”
						content = "ECHO:\t" + content;
						// content编码,再包裹成ByteBuffer。
						buffer = ByteBuffer.wrap(content.getBytes("UTF-8"));
						// 最后有意义的一步,发送。
						clientChannel.write(buffer);
						// 完成使命,关闭通道。
						clientChannel.close();						
					}
										
				}  catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					key.cancel();
					try {
						key.channel().close();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
				
			}
		}
		
	}

}

客户端实现

package com.xieyincai.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class SimpleTCPClient {

	private final static String HOSTNAME = "localhost";
	private final static int PORT = 8000;
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Socket socket = new Socket();
		
		try {
			SocketAddress address = new InetSocketAddress(HOSTNAME, PORT);
			socket.connect(address);
			
			OutputStream out = socket.getOutputStream();
			InputStream in = socket.getInputStream();
			
			String data = String.valueOf(System.nanoTime());
			System.out.println(data);
			
			out.write(data.getBytes("UTF-8"));
			out.flush();
			
			byte[] buffer = new byte[4096];
			int length = in.read(buffer);
			String result = new String(buffer, 0, length, "UTF-8");
			
			System.out.println(result);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

服务器端输出:

CLIENT:	/127.0.0.1 : 51979
77667180634774
CLIENT:	/127.0.0.1 : 51995
77670570107738
CLIENT:	/127.0.0.1 : 52004
77671463497729
CLIENT:	/127.0.0.1 : 52007
77672178722326
CLIENT:	/127.0.0.1 : 52009
77673039094364

客户端输出:

77673039094364
ECHO:	77673039094364

Leave a Reply

Your email address will not be published. Required fields are marked *