`
tmdpzc
  • 浏览: 13390 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

java NIO 学习 聊天室程序 (1)

阅读更多
学习 java nio ,自己建立一个 nio的聊天室程序。
服务器采用单一线程 轮训,accept的所有端口。

首先定义一个接口,处理 SelectionKey 上的事件。

static interface NioHandler {
		/**
		 * 处理{@link SelectionKey#OP_ACCEPT}事件
		 * 
		 * @param key
		 * @throws IOException
		 */
		void handleAccept(SelectionKey key) throws IOException;

		/**
		 * 处理{@link SelectionKey#OP_READ}事件
		 * 
		 * @param key
		 * @throws IOException
		 */
		void handleRead(SelectionKey key) throws IOException;

		/**
		 * 处理{@link SelectionKey#OP_WRITE}事件
		 * 
		 * @param key
		 * @throws IOException
		 */
		void handleWrite(SelectionKey key) throws IOException;
		/**
		 * 处理IO 异常事件
		 * @param key 
		 * @throws IOException
		 */
		void handleIoError(SelectionKey key) throws IOException;
	}

服务器的run方法中处理io事件


	@Override
	public void run() {
		InetSocketAddress address = new InetSocketAddress(mConfig.port);
		try {
			mSSC  = ServerSocketChannel.open();
			Selector selector = Selector.open();
			mSSC.socket().bind(address);
			mSSC.configureBlocking(false);
			mSSCKey = mSSC.register(selector, SelectionKey.OP_ACCEPT);
			while (true) {
				int events = selector.select();
				if (events == 0) {
					continue; //忽略
				}
				Iterator<SelectionKey> it = selector.selectedKeys().iterator();
				while(it.hasNext()) {
					SelectionKey key = it.next();
					it.remove();
					if (key.isAcceptable()) {
						mIoHandler.handleAccept(key);
					} else if (key.isReadable()) {
						mIoHandler.handleRead(key);
					} else if (key.isWritable()) {
						mIoHandler.handleWrite(key);
					}else {
						break;
					}
				}
				Thread.yield();// 让出执行空间
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new ChatServerException(e);
		}

	}


IoHandler的实现类: ChatIoHandler
class ChatIoHandler implements NioHandler {
		StringBuffer sb = new StringBuffer();

		@Override
		public void handleAccept(SelectionKey key) throws IOException {
			ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
			SocketChannel client = ssc.accept();
			if (client != null) {
				client.configureBlocking(false);
				System.out.println("Accept:"
						+ client.socket().getRemoteSocketAddress().toString());
				client.register(key.selector(), SelectionKey.OP_READ);
				String address = client.socket().getRemoteSocketAddress()
						.toString();
				ChatServer.this.mChannelMap.put(address, client); // 加入map中
			}
			
		}

		@Override
		public void handleRead(SelectionKey key) throws IOException {
			SocketChannel sc = (SocketChannel) key.channel();
			System.out.println("Read from"
					+ sc.socket().getRemoteSocketAddress() + " ");
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			buffer.clear();
			int len = 0;
			StringBuffer sBuffer = new StringBuffer();
			while ((len = sc.read(buffer)) > 0) {
				buffer.flip();
				sBuffer.append(new String(buffer.array(), 0, len));
			}
			//handle socket error;
			if (len < 0) {
				handleIoError(key);
			}
			String msg = sBuffer.toString();
			if (sBuffer.length() > 0) {
				System.out.println("Server receive:" + sBuffer.toString());
			}
			//command 设计模式
			String[] args = msg.split(" ");
			Command cmd = CommandFactory.getInstance().createCommand(args[0]);
			cmd.handleCmd(key, ChatServer.this,args);
		}

		@Override
		public void handleWrite(SelectionKey key) throws IOException {
			// TODO Auto-generated method stub
			SocketChannel  sc = (SocketChannel) key.channel();
			String address = sc.socket().getRemoteSocketAddress().toString();
			System.out.println("handleWrite: " + address);
			String msg = (String) key.attachment();
			if (msg != null) {
				int len = sc.write(ByteBuffer.wrap(msg.getBytes()));
				if (len < 0) {
					//handle error;
					handleIoError(key);
				}
				key.attach(null);
				key.interestOps(SelectionKey.OP_READ);
			}
		}

accept 一个 channel 后,向channel注册server端的 selector ,将感兴趣的事件设为OP_READ
并 将该channel 以socket address  为key 保存在Server端的一个map中。

handleRead中 使用 command 设计模式,将client端穿来的参数 args 解析为命令,调用具体的命令处理方法。

command 抽象类:

package com.tcl.chat.command;

import java.io.IOException;
import java.nio.channels.SelectionKey;

import com.tcl.chat.ChatServer;

public abstract class Command {
	public abstract void handleCmd(SelectionKey key , ChatServer server,String[] args) throws IOException;
}



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics