Java NIO简易聊天室
NIO方式实现简易通讯。代码注释较少,将就看看吧。
哎,越来越懒了。也没什么东西可写的,直接贴贴代码==。不过,有附件工程,觉着这都没什么必要。
运行效果不弄了,可以看后一篇《Android NIO简易聊天室》。核心的没变,就是包了层Android的简单界面。
一、Chat.java
服务器、客户端的基类。继承Observable,作为被观察者。定义些状态啊什么的。
- public abstract class Chat extends Observable {
- public static final int SEV_ON = 0;
- public static final int SEV_OFF = 1;
- public static final int CLT_CONNECT = 2;
- public static final int CLT_DISCONNECT = 3;
- public static final int MSG_SEND = 4;
- public static final int MSG_RECEIVE = 5;
- public static final int ERROR = 6;
- /** 缓存区大小 */
- protected static final int BUFFERSIZE = 1024 * 10;
- /** 字符编码 */
- protected static final String CHARSET = "UTF-8";
- /** 字符编码器 */
- protected static CharsetEncoder encoder;
- /** 字符解码器 */
- protected static CharsetDecoder decoder;
- static {
- encoder = Charset.forName(CHARSET).newEncoder();
- decoder = Charset.forName(CHARSET).newDecoder();
- }
- /** 当前状态 */
- protected int status;
- /** 获得当前状态 */
- public int getStatus() {
- return status;
- }
- /**
- * 通知状态改变
- * @param status 状态
- * @param arg 参数
- */
- protected void notifyStateChanged(int status, Object arg) {
- this.status = status;
- notifyStateChanged(arg);
- }
- /**
- * 通知状态改变
- * @param arg 参数
- */
- protected void notifyStateChanged(Object arg) {
- setChanged();
- notifyObservers(arg);
- }
- }
二、ChatServer.java
服务器核心类。
- public class ChatServer extends Chat implements Runnable {
- private boolean isPrepared = false;
- private ServerSocketChannel ssc;
- private Selector selector;
- private ArrayList<SelectionKey> serverKeyList;
- private String receiveMessage;
- /**
- * 服务器构造函数
- *
- * @param port 端口
- */
- public ChatServer(int port) {
- try {
- selector = Selector.open();
- ssc = ServerSocketChannel.open();
- ssc.configureBlocking(false);
- ssc.socket().bind(new InetSocketAddress(port));
- ssc.register(selector, SelectionKey.OP_ACCEPT);
- serverKeyList = new ArrayList<SelectionKey>();
- isPrepared = true;
- } catch (IOException e) {
- notifyStateChanged(ERROR, e);
- e.printStackTrace();
- }
- }
- public void start() {
- if (isPrepared)
- new Thread(this).start();
- }
- /** 针对同一个SelectionKey在一次写操作之前,后attach进去的消息会被覆盖。 */
- public void send(String msg, InetSocketAddress... toIps) {
- notifyStateChanged(MSG_SEND, msg);
- if (null == serverKeyList || serverKeyList.size() <= 0) {
- return;
- }
- if (null != toIps && toIps.length >= 1) {
- /* 发送给部分 */
- for (SelectionKey serverKey : serverKeyList) {
- SocketChannel sc = (SocketChannel) serverKey.channel();
- SocketAddress ip = sc.socket().getRemoteSocketAddress();
- for (InetSocketAddress toIp : toIps) {
- if (toIp.equals(ip)) {
- serverKey.attach(msg);
- serverKey.interestOps(SelectionKey.OP_READ
- | SelectionKey.OP_WRITE);
- serverKey.selector().wakeup();
- break;
- }
- }
- }
- } else {
- /* 发送给全部 */
- for (SelectionKey serverKey : serverKeyList) {
- serverKey.attach(msg);
- serverKey.interestOps(SelectionKey.OP_READ
- | SelectionKey.OP_WRITE);
- serverKey.selector().wakeup();
- }
- }
- }
- @Override
- public void run() {
- notifyStateChanged(SEV_ON, null);
- try {
- while (isPrepared) {
- int keysCount = selector.select();
- if (keysCount < 1) {
- continue;
- }
- Set<SelectionKey> set = selector.selectedKeys();
- Iterator<SelectionKey> it = set.iterator();
- while (it.hasNext()) {
- SelectionKey key = it.next();
- if (key.isAcceptable()) {
- doAccept(key);
- }
- if (key.isValid() && key.isReadable()) {
- doRead(key);
- }
- if (key.isValid() && key.isWritable()) {
- doWrite(key);
- }
- }
- set.clear();
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- // close();
- notifyStateChanged(SEV_OFF, null);
- }
- }
- public void close() {
- isPrepared = false;
- try {
- if (null != serverKeyList) {
- for (SelectionKey key : serverKeyList) {
- key.channel().close();
- }
- }
- if (null != selector) {
- selector.close();
- }
- if (null != ssc) {
- ssc.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- private void doAccept(SelectionKey key) {
- ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
- try {
- SocketChannel sc = ssc.accept();
- sc.configureBlocking(false);
- SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
- // newKey.attach(new ArrayList<String>());
- serverKeyList.add(newKey);
- notifyStateChanged(CLT_CONNECT, sc.socket()
- .getRemoteSocketAddress());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- private void doRead(SelectionKey key) {
- SocketChannel sc = (SocketChannel) key.channel();
- ByteBuffer bb = ByteBuffer.allocate(BUFFERSIZE);
- StringBuffer sb = new StringBuffer();
- try {
- int count = 0;
- while ((count = sc.read(bb)) > 0) {
- bb.flip();
- sb.append(decoder.decode(bb));
- }
- if (count == -1) {
- disconnect(key, sc);
- } else {
- receiveMessage = sb.toString().trim();
- notifyStateChanged(MSG_RECEIVE, sc.socket()
- .getRemoteSocketAddress());
- }
- } catch (IOException e) {
- disconnect(key, sc);
- // e.printStackTrace();
- }
- }
- private void doWrite(SelectionKey key) {
- SocketChannel sc = (SocketChannel) key.channel();
- String msg = (String) key.attachment();
- if (null == msg) {
- key.interestOps(SelectionKey.OP_READ);
- return;
- }
- try {
- sc.write(encoder.encode(CharBuffer.wrap(msg)));
- } catch (IOException e) {
- disconnect(key, sc);
- // e.printStackTrace();
- }
- key.interestOps(SelectionKey.OP_READ);
- }
- /** 断开连接 */
- private void disconnect(SelectionKey key, SocketChannel sc) {
- serverKeyList.remove(key);
- notifyStateChanged(CLT_DISCONNECT, sc.socket().getRemoteSocketAddress());
- try {
- key.cancel();
- sc.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public String getReceiveMessage() {
- return receiveMessage;
- }
- }
三、ChatClient.java
客户端核心类。
- public class ChatClient extends Chat implements Runnable {
- private boolean isPrepared = false;
- private Selector selector;
- private SelectionKey clientKey;
- private InetSocketAddress address;
- /**
- * 客户端构造函数
- *
- * @param host 服务器地址
- * @param port 服务器端口
- */
- public ChatClient(String host, int port) {
- address = new InetSocketAddress(host, port);
- try {
- selector = Selector.open();
- } catch (IOException e) {
- notifyStateChanged(ERROR, e);
- e.printStackTrace();
- }
- }
- public void start() {
- new Thread(this).start();
- }
- public void send(String msg) {
- notifyStateChanged(MSG_SEND, msg);
- if (null == clientKey) {
- return;
- }
- clientKey.attach(msg);
- clientKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
- clientKey.selector().wakeup();
- }
- @Override
- public void run() {
- try {
- SocketChannel sc = SocketChannel.open();
- sc.configureBlocking(false);
- sc.connect(address);
- clientKey = sc.register(selector, SelectionKey.OP_CONNECT);
- isPrepared = true;
- while (isPrepared) {
- int keysCount = selector.select();
- if (keysCount < 1) {
- continue;
- }
- Set<SelectionKey> set = selector.selectedKeys();
- Iterator<SelectionKey> it = set.iterator();
- while (it.hasNext()) {
- SelectionKey key = it.next();
- if (key.isConnectable()) {
- doConnect(key);
- }
- if (key.isValid() && key.isReadable()) {
- doRead(key);
- }
- if (key.isValid() && key.isWritable()) {
- doWrite(key);
- }
- }
- set.clear();
- }
- } catch (IOException e) {
- notifyStateChanged(ERROR, e);
- e.printStackTrace();
- } finally {
- // close();
- notifyStateChanged(CLT_DISCONNECT, null);
- }
- }
- public void close() {
- isPrepared = false;
- try {
- if (null != clientKey) {
- clientKey.channel().close();
- }
- if (null != selector) {
- selector.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- private void doConnect(SelectionKey key) {
- SocketChannel sc = (SocketChannel) key.channel();
- try {
- // http://www.velocityreviews.com/forums/t145075-whats-the-proper-way-to-use-socketchannel-finishconnect.html
- sc.finishConnect();
- key.interestOps(SelectionKey.OP_READ);
- notifyStateChanged(CLT_CONNECT, null);
- } catch (IOException e) {
- disconnect(key);
- e.printStackTrace();
- }
- }
- private void doRead(SelectionKey key) {
- SocketChannel sc = (SocketChannel) key.channel();
- ByteBuffer bb = ByteBuffer.allocate(BUFFERSIZE);
- StringBuffer sb = new StringBuffer();
- try {
- int count = 0;
- while ((count = sc.read(bb)) > 0) {
- bb.flip();
- sb.append(decoder.decode(bb));
- }
- if (count == -1) {
- disconnect(key);
- } else {
- notifyStateChanged(MSG_RECEIVE, sb.toString().trim());
- }
- } catch (IOException e) {
- disconnect(key);
- e.printStackTrace();
- }
- }
- private void doWrite(SelectionKey key) {
- SocketChannel sc = (SocketChannel) key.channel();
- String msg = (String) key.attachment();
- if (null == msg) {
- key.interestOps(SelectionKey.OP_READ);
- return;
- }
- try {
- sc.write(encoder.encode(CharBuffer.wrap(msg)));
- } catch (IOException e) {
- disconnect(key);
- e.printStackTrace();
- }
- key.interestOps(SelectionKey.OP_READ);
- }
- /** 断开连接 */
- private void disconnect(SelectionKey key) {
- notifyStateChanged(CLT_DISCONNECT, null);
- try {
- key.cancel();
- key.channel().close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/921770,如需转载请自行联系原作者