Java NIO (二)

 在Java NIO (一)中看到了在不使用Selector的情况下的TCP服务器的搭建和客户端的调用,其实在实际的项目开发中很少这样搭建TCP服务器的,即使使用Selector也不常见。下面是使用Selector简单模拟TCP服务器的示例:

1,服务器类:Server.java

package com.zws.nio.sel;

import java.io.IOException;
import java.net.InetSocketAddress;
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 com.zws.nio.util.NIOHelper;

public class Server extends Thread{

	private int port;
	private boolean block = false;
	private ServerSocketChannel sverChn;
	private SocketChannel chn;
	private Selector sel;

	public Server(int port) {
		this.port = port;
		init();
	}

	public Server(int port, boolean block) {
		this.port = port;
		this.block = block;
		init();
	}

	private void init() {
		try {
			sel = Selector.open();
			sverChn = ServerSocketChannel.open();
			//配置阻塞,false:非阻塞,true:阻塞。
			sverChn.configureBlocking(block);
			//注意:ServerSocketChannel也有一个bind方法,但此处我们用socket的bind方法。
			sverChn.socket().bind(new InetSocketAddress(port));
			//此处注册为ACCEPT事件,即此时应该对是否准备好接受链接。
			sverChn.register(sel, SelectionKey.OP_ACCEPT);
			
			System.out.println("Server init success...");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	public void run() {
		//此处用线程被打断作为终止信号。
		while (!isInterrupted()) {
			try {
				/**
				 * selectNow:此方法为非阻塞方法,不管监听(感兴趣)事件是否到达都会返回。
				 * 方法返回int值,表示监听到事件的数量。
				 * 如果有监听事件到达则将对应事件的SelectionKey放入集合。
				 * 与此方法具有相似功能的方法有:
				 * 1,select()   此方法会阻塞,直到监听事件到达才返回。
				 * 2,select(long timeout)  此方法阻塞指定时间然后返回。
				 */
				if (sel.selectNow() > 0) {
					//前面select方法会将SelectionKey放入此集合
					Iterator<SelectionKey> itor = sel.selectedKeys().iterator();
					while (itor.hasNext()) {
						SelectionKey key = itor.next();
						if (key.isAcceptable()) {
							handleAccess(key);
						} else {
							chn = (SocketChannel) key.channel();
							if (key.isReadable()) {
								handleRead(key);
							} else if (key.isWritable()) {
								handleWrite(key);
							}
						}
						itor.remove();//移除处理过的SelectionKey,否则会重复处理。
					}
				} else {
					//do something else.
				}
				
			} catch (IOException e) {
				e.printStackTrace();
				closeChnnel();
			} 
		}
	}
	/**
	 * 处理accept
	 * @param key
	 * @throws IOException
	 */
	private void handleAccess(SelectionKey key) throws IOException {
		sverChn = (ServerSocketChannel) key.channel();
		chn = sverChn.accept();
		chn.configureBlocking(block);
		chn.register(sel, SelectionKey.OP_READ);
	}
	/*
	 * 处理读数据
	 */
	private void handleRead(SelectionKey key) throws IOException {
		String msg = NIOHelper.read(chn);
		System.err.println("server receive msg:" + msg);
		key.interestOps(SelectionKey.OP_WRITE);
	}
	/*
	 * 处理写数据
	 */
	private void handleWrite(SelectionKey key) throws IOException {
		NIOHelper.write(chn, "Hello socketchannel");
		chn.close();
	}
	/*
	 * 关闭SocketChannel
	 */
	private void closeChnnel() {
		try {
			if (chn != null)
				chn.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("SocketChannel is closed...");
	}
	/*
	 * 关闭ServerSocketChannel
	 */
	private void closeServerChannel() {
		try {
			if (sverChn != null)
				sverChn.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("ServerSocketChannel is closed...");
	}

	/**
	 * 关闭服务器
	 */
	public void close() {
		interrupt();
		closeChnnel();
		closeServerChannel();
		System.out.println("Sever is closed...");
	}

	public static void main(String[] args) {
		Server server = new Server(9003);
		server.start();
		//server.close();//关闭服务器
	}
}

2,客户端类:Client.java

package com.zws.nio.sel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import com.zws.nio.util.IntegerUtil;
import com.zws.nio.util.NIOHelper;

public class Client {
	/**
	 * NIO方式访问
	 * @throws IOException
	 */
	public void listenWrite() throws IOException {
		Selector sel = Selector.open();
		
		SocketChannel chn = SocketChannel.open();
		//以下两句顺序不可颠倒,否则会出现死锁。
		chn.connect(new InetSocketAddress("localhost", 9003));
		chn.configureBlocking(false);
		
		
		chn.register(sel, SelectionKey.OP_WRITE);
		boolean isOver = false;
		
		while (true) {
			sel.select();
			
			Iterator<SelectionKey> itor = sel.selectedKeys().iterator();
			while (itor.hasNext()) {
				SelectionKey key = itor.next();
				chn = (SocketChannel) key.channel();
				
				if (key.isWritable()) {
					String msg = "Hello ServerSocketChannel...";
					NIOHelper.write(chn, msg);
					key.interestOps(SelectionKey.OP_READ);
				} else if (key.isReadable()) {
					String msg = NIOHelper.read(chn);
					System.out.println("client receive msg:" + msg);
					chn.close();
					isOver = true;
				}
					
				itor.remove();
			}
			if (isOver) 
				break;
		}
	}
	/**
	 * IO方式访问
	 * @throws IOException
	 */
	public void listenIO() throws IOException {
		String charsetName = "UTF-8";
		Socket socket = new Socket("localhost", 9003);
		OutputStream out = socket.getOutputStream();
		String msg = "Hello I am socket";
		byte[] bts = msg.getBytes(charsetName);
		out.write(IntegerUtil.toBytes(bts.length));
		out.write(bts);
		out.flush();
		
		InputStream in = socket.getInputStream();
		bts = new byte[4];
		in.read(bts);
		int size = IntegerUtil.toInt(bts);
		bts = new byte[size];
		in.read(bts);
		msg = new String(bts,charsetName);
		System.out.println("socket receive msg:" + msg);
		
		in.close();
		out.close();
		socket.close();
	}
	
	
	
	public static void main(String[] args) throws IOException {
		new Client().listenWrite();
		//new Client().listenIO();
	}

}


你可能感兴趣的:(java,nio)