天天看点

Java NIO简易聊天室(一)

 <b>Java NIO</b><b>简易聊天室</b>

         NIO方式实现简易通讯。代码注释较少,将就看看吧。

         哎,越来越懒了。也没什么东西可写的,直接贴贴代码==。不过,有附件工程,觉着这都没什么必要。

<b>一、</b><b>Chat.java</b>

         服务器、客户端的基类。继承Observable,作为被观察者。定义些状态啊什么的。

 <b></b>

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); 

    protected void notifyStateChanged(Object arg) { 

       setChanged(); 

       notifyObservers(arg); 

<b>二、</b><b>ChatServer.java</b>

         服务器核心类。

<b></b>

public class ChatServer extends Chat implements Runnable { 

    private boolean isPrepared = false; 

    private ServerSocketChannel ssc; 

    private Selector selector; 

    private ArrayList&lt;SelectionKey&gt; 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&lt;SelectionKey&gt;(); 

           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() &lt;= 0) { 

           return; 

       if (null != toIps &amp;&amp; toIps.length &gt;= 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 { 

           /* 发送给全部 */ 

              serverKey.attach(msg); 

              serverKey.interestOps(SelectionKey.OP_READ 

                     | SelectionKey.OP_WRITE); 

              serverKey.selector().wakeup(); 

    @Override 

    public void run() { 

       notifyStateChanged(SEV_ON, null); 

           while (isPrepared) { 

              int keysCount = selector.select(); 

              if (keysCount &lt; 1) { 

                  continue; 

              Set&lt;SelectionKey&gt; set = selector.selectedKeys(); 

              Iterator&lt;SelectionKey&gt; it = set.iterator(); 

              while (it.hasNext()) { 

                  SelectionKey key = it.next(); 

                  if (key.isAcceptable()) { 

                     doAccept(key); 

                  if (key.isValid() &amp;&amp; key.isReadable()) { 

                     doRead(key); 

                  if (key.isValid() &amp;&amp; key.isWritable()) { 

                     doWrite(key); 

              set.clear(); 

       } finally { 

           // close(); 

           notifyStateChanged(SEV_OFF, null); 

    public void close() { 

       isPrepared = false; 

           if (null != serverKeyList) { 

              for (SelectionKey key : serverKeyList) { 

                  key.channel().close(); 

           if (null != selector) { 

              selector.close(); 

           if (null != ssc) { 

              ssc.close(); 

    private void doAccept(SelectionKey key) { 

       ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); 

           SocketChannel sc = ssc.accept(); 

           sc.configureBlocking(false); 

           SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ); 

           // newKey.attach(new ArrayList&lt;String&gt;()); 

           serverKeyList.add(newKey); 

           notifyStateChanged(CLT_CONNECT, sc.socket() 

                  .getRemoteSocketAddress()); 

    private void doRead(SelectionKey key) { 

       SocketChannel sc = (SocketChannel) key.channel(); 

       ByteBuffer bb = ByteBuffer.allocate(BUFFERSIZE); 

       StringBuffer sb = new StringBuffer(); 

           int count = 0; 

           while ((count = sc.read(bb)) &gt; 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()); 

           disconnect(key, sc); 

           // e.printStackTrace(); 

    private void doWrite(SelectionKey key) { 

       String msg = (String) key.attachment(); 

       if (null == msg) { 

           key.interestOps(SelectionKey.OP_READ); 

           sc.write(encoder.encode(CharBuffer.wrap(msg))); 

       key.interestOps(SelectionKey.OP_READ); 

    /** 断开连接 */ 

    private void disconnect(SelectionKey key, SocketChannel sc) { 

       serverKeyList.remove(key); 

       notifyStateChanged(CLT_DISCONNECT, sc.socket().getRemoteSocketAddress()); 

           key.cancel(); 

           sc.close(); 

    public String getReceiveMessage() { 

       return receiveMessage; 

<b>三、</b><b>ChatClient.java</b>

         客户端核心类。

public class ChatClient extends Chat implements Runnable { 

    private SelectionKey clientKey; 

    private InetSocketAddress address; 

     * 客户端构造函数 

     * @param host 服务器地址 

     * @param port 服务器端口 

    public ChatClient(String host, int port) { 

       address = new InetSocketAddress(host, port); 

       new Thread(this).start(); 

    public void send(String msg) { 

       if (null == clientKey) { 

       clientKey.attach(msg); 

       clientKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); 

       clientKey.selector().wakeup(); 

           SocketChannel sc = SocketChannel.open(); 

           sc.connect(address); 

           clientKey = sc.register(selector, SelectionKey.OP_CONNECT); 

                  if (key.isConnectable()) { 

                     doConnect(key); 

           notifyStateChanged(CLT_DISCONNECT, null); 

           if (null != clientKey) { 

              clientKey.channel().close(); 

    private void doConnect(SelectionKey key) { 

           // http://www.velocityreviews.com/forums/t145075-whats-the-proper-way-to-use-socketchannel-finishconnect.html 

           sc.finishConnect(); 

           notifyStateChanged(CLT_CONNECT, null); 

           disconnect(key); 

              disconnect(key); 

              notifyStateChanged(MSG_RECEIVE, sb.toString().trim()); 

    private void disconnect(SelectionKey key) { 

       notifyStateChanged(CLT_DISCONNECT, null); 

           key.channel().close(); 

     本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/921770,如需转载请自行联系原作者

继续阅读