Java NIO简易聊天室(一)

 

四、 Message.java
         定义的字符串消息。

 
  
  1. public class Message { 
  2.   
  3.     static class MessageHolder { 
  4.        static Message instance = new Message(); 
  5.     } 
  6.   
  7.     public static Message getInstance() { 
  8.        return MessageHolder.instance; 
  9.     } 
  10.   
  11.     public static final String SEPARATOR = "`"
  12.   
  13.     /** 客户端登录服务器的消息类型,用以通知登录名等 */ 
  14.     public static final int MSG_1 = 1// 1`msg 
  15.     /** 服务器通知各客户端新登录者信息的消息类型 */ 
  16.     public static final int MSG_2 = 2// 2`msg`fromIp 
  17.     /** 服务器回复新登录者所有在线客户端信息的消息类型 */ 
  18.     public static final int MSG_3 = 3// 3`ip`msg`ip`msg`... 
  19.     /** 客户端发送所有人信息的消息类型(或服务器发送消息) */ 
  20.     public static final int MSG_4 = 4// 4`msg 
  21.     /** 客户端发送指定人信息的消息类型 */ 
  22.     public static final int MSG_5 = 5// 5`msg`toIp`toIp`... 
  23.     /** 服务器转发发送者信息的消息类型 */ 
  24.     public static final int MSG_6 = 6// 6`msg`fromIp 
  25.     /** 客户端通知服务器退出的消息类型 */ 
  26.     public static final int MSG_7 = 7// 7 
  27.     /** 服务器通知各客户端退出者信息的消息类型 */ 
  28.     public static final int MSG_8 = 8// 8`fromIp 
  29.   
  30.     // 构造和解析方式详细参见附件工程了 
  31.   
 
五、 ChatServerTest.java
         服务器消息处理实现。

 
  
  1. public class ChatServerTest implements Observer, Runnable { 
  2.   
  3.     private static final String BLANK = " "
  4.   
  5.     /** 在线客户端信息 */ 
  6.     private HashMap<InetSocketAddress, String> onLineMap; 
  7.   
  8.     private ChatServer server; 
  9.   
  10.     public ChatServerTest() { 
  11.        server = new ChatServer(8899); 
  12.        server.addObserver(this); 
  13.        server.start(); 
  14.        new Thread(this).start(); 
  15.     } 
  16.   
  17.     @Override 
  18.     public void update(Observable o, Object arg) { 
  19.        ChatServer server = (ChatServer) o; 
  20.        switch (server.getStatus()) { 
  21.        case ChatServer.SEV_ON: 
  22.            System.out.println("服务器开启了"); 
  23.            onLineMap = new HashMap<InetSocketAddress, String>(); 
  24.            break
  25.        case ChatServer.SEV_OFF: 
  26.            System.out.println("服务器关闭了"); 
  27.            onLineMap = null
  28.            break
  29.        case ChatServer.CLT_CONNECT: 
  30.            // InetSocketAddress address = (InetSocketAddress) arg; 
  31.  
  32.            break
  33.        case ChatServer.CLT_DISCONNECT: 
  34.            quit((InetSocketAddress) arg); 
  35.            break
  36.        case ChatServer.MSG_SEND: 
  37.            // System.out.println("通知:" 
  38.  
  39.            // + ((String) arg).split(Message.SEPARATOR)[1]); 
  40.            break
  41.        case ChatServer.MSG_RECEIVE: 
  42.            Message msg = Message.getInstance(); 
  43.            msg.create(server.getReceiveMessage()); 
  44.            msg.setFromIp((InetSocketAddress) arg); 
  45.            handleMsg(msg); // 处理消息 
  46.            break
  47.        case ChatServer.ERROR: 
  48.            System.out.println("error : " + ((Exception) arg).getMessage()); 
  49.  
  50.            break
  51.        } 
  52.     } 
  53.   
  54.     private void handleMsg(Message msg) { 
  55.        int type = msg.getType(); 
  56.        InetSocketAddress formIp = msg.getFromIp(); 
  57.        switch (type) { 
  58.        case Message.MSG_1: 
  59.            System.out.println(msg.getMsg() + BLANK + msg.toIpString(formIp) 
  60.                   + BLANK + "登录了"); 
  61.            onLineMap.put(formIp, msg.getMsg()); 
  62.            /* 通知所有客户端新登录者信息 */ 
  63.            msg.setType(Message.MSG_2); 
  64.            // msg.setFromIp(address); 
  65.  
  66.            server.send(msg.toString()); 
  67.            /* 返回登录客户端所有在线客户端信息(会覆盖前一个发送给address的信息) */ 
  68.            msg.setType(Message.MSG_3); 
  69.            msg.setOnLineMap(onLineMap); 
  70.            server.send(msg.toString(), formIp); 
  71.            break
  72.        case Message.MSG_4: 
  73.            System.out.println(onLineMap.get(msg.getFromIp()) + BLANK + "said" 
  74.                   + BLANK + msg.getMsg()); 
  75.            msg.setType(Message.MSG_6); 
  76.            server.send(msg.toString()); 
  77.            break
  78.        case Message.MSG_7: 
  79.            quit(formIp); 
  80.            break
  81.        } 
  82.     } 
  83.   
  84.     private void quit(InetSocketAddress address) { 
  85.        if (onLineMap.get(address) != null) { 
  86.            System.out 
  87.                   .println(onLineMap.get(address) + BLANK 
  88.                          + Message.getInstance().toIpString(address) + BLANK 
  89.  
  90.                          + "退出了"); 
  91.            onLineMap.remove(address); 
  92.            Message msg = Message.getInstance(); 
  93.            msg.setType(Message.MSG_8); 
  94.            msg.setFromIp(address); 
  95.            server.send(msg.toString()); 
  96.        } 
  97.     } 
  98.   
  99.     @Override 
  100.     public void run() { 
  101.        BufferedReader br = null
  102.        try { 
  103.            br = new BufferedReader(new InputStreamReader(System.in)); 
  104.            while (true) { 
  105.               String inputLine = br.readLine(); 
  106.               if (inputLine.trim().toLowerCase().equals("off")) { 
  107.                   server.close(); 
  108.                   break
  109.               } else { 
  110.                   // 键盘输入为GBK编码,需要转成UTF-8才可显示。或者变为GBK工程。 
  111.                   // 这么转码仍是有问题的。例如奇数汉字“大家好”时,显示为“大家�?”。可以看下如下网址: 
  112.                   // http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html 
  113.  
  114.                   inputLine = new String(inputLine.getBytes("gbk"), "utf-8"); 
  115.                   server.send(Message.MSG_4 + Message.SEPARATOR + inputLine); 
  116.                   System.out.println("通知:" + inputLine); 
  117.               } 
  118.            } 
  119.         } catch (IOException e) { 
  120.            e.printStackTrace(); 
  121.        } finally { 
  122.            try { 
  123.               if (br != null) { 
  124.                   br.close(); 
  125.               } 
  126.            } catch (IOException e) { 
  127.               e.printStackTrace(); 
  128.            } 
  129.        } 
  130.     } 
  131.   
  132.     public static void main(String[] args) { 
  133.        new ChatServerTest(); 
  134.     } 
  135.   
 
六、 ChatClientTest.java
         客户端消息处理实现。

 
  
  1. public class ChatClientTest implements Observer, Runnable { 
  2.   
  3.     private static final String BLANK = " "
  4.   
  5.     /** 在线客户端信息 */ 
  6.     private HashMap<InetSocketAddress, String> onLineMap; 
  7.   
  8.     private String name; 
  9.     private ChatClient client; 
  10.   
  11.     public ChatClientTest(String name) { 
  12.        this.onLineMap = new HashMap<InetSocketAddress, String>(); 
  13.        this.name = name; 
  14.        client = new ChatClient("192.168.1.107"8899); 
  15.        // client = new ChatClient("127.0.0.1", 8899); 
  16.  
  17.        client.addObserver(this); 
  18.         client.start(); 
  19.        new Thread(this).start(); 
  20.     } 
  21.   
  22.     @Override 
  23.     public void update(Observable o, Object arg) { 
  24.        ChatClient client = (ChatClient) o; 
  25.        switch (client.getStatus()) { 
  26.        case ChatClient.CLT_CONNECT: 
  27.            // 发送登录消息 
  28.            Message msg1 = Message.getInstance(); 
  29.            msg1.setType(Message.MSG_1); 
  30.            msg1.setMsg(name); 
  31.            client.send(msg1.toString()); 
  32.            break
  33.        case ChatClient.CLT_DISCONNECT: 
  34.            System.out.println("断开服务器连接"); 
  35.            System.exit(1); 
  36.            break
  37.        case ChatClient.MSG_SEND: 
  38.            break
  39.        case ChatClient.MSG_RECEIVE: 
  40.            Message msg = Message.getInstance(); 
  41.            msg.create((String) arg); 
  42.            handleMsg(msg); // 处理消息 
  43.            break
  44.        case ChatClient.ERROR: 
  45.            System.out.println("error : " + ((Exception) arg).getMessage()); 
  46.  
  47.            break
  48.        } 
  49.     } 
  50.   
  51.     private void handleMsg(Message msg) { 
  52.        int type = msg.getType(); 
  53.        switch (type) { 
  54.        case Message.MSG_2: 
  55.            System.out.println(msg.getMsg() + BLANK 
  56.                   + msg.toIpString(msg.getFromIp()) + BLANK + "登录了"); 
  57.            onLineMap.put(msg.getFromIp(), msg.getMsg()); 
  58.            break
  59.        case Message.MSG_3: 
  60.            onLineMap = msg.getOnLineMap(); 
  61.            System.out.println("连接上了服务器"); 
  62.            System.out.println("/***在线成员***/"); 
  63.            Iterator<Entry<InetSocketAddress, String>> it = onLineMap 
  64.  
  65.                   .entrySet().iterator(); 
  66.            while (it.hasNext()) { 
  67.               Entry<InetSocketAddress, String> entry = it.next(); 
  68.  
  69.               InetSocketAddress address = entry.getKey(); 
  70.               System.out.println(entry.getValue() + BLANK 
  71.                      + address.getAddress().getHostAddress() + ":" 
  72.                      + address.getPort()); 
  73.            } 
  74.            System.out.println("/***在线成员***/"); 
  75.            break
  76.        case Message.MSG_4: 
  77.            System.out.println("通知:" + msg.getMsg()); 
  78.            break
  79.        case Message.MSG_6: 
  80.            System.out.println(onLineMap.get(msg.getFromIp()) + BLANK + "said" 
  81.                   + BLANK + msg.getMsg()); 
  82.            break
  83.        case Message.MSG_8: 
  84.            InetSocketAddress address = msg.getFromIp(); 
  85.            System.out 
  86.                   .println(onLineMap.get(address) + BLANK 
  87.                          + Message.getInstance().toIpString(address) + BLANK 
  88.  
  89.                          + "退出了"); 
  90.            onLineMap.remove(address); 
  91.            break
  92.        } 
  93.     } 
  94.   
  95.     @Override 
  96.     public void run() { 
  97.        BufferedReader br = null
  98.        try { 
  99.            br = new BufferedReader(new InputStreamReader(System.in)); 
  100.            while (true) { 
  101.               String inputLine = br.readLine(); 
  102.               if (inputLine.trim().toLowerCase().equals("quit")) { 
  103.                   client.send(Message.MSG_7 + ""); 
  104.                   client.close(); 
  105.                   break
  106.               } else { 
  107.                   // 参见ChatServerTest注释 
  108.                   inputLine = new String(inputLine.getBytes("gbk"), "utf-8"); 
  109.                   client.send(Message.MSG_4 + Message.SEPARATOR + inputLine); 
  110.               } 
  111.            } 
  112.        } catch (IOException e) { 
  113.            e.printStackTrace(); 
  114.        } finally { 
  115.            try { 
  116.               if (br != null) { 
  117.                   br.close(); 
  118.               } 
  119.            } catch (IOException e) { 
  120.               e.printStackTrace(); 
  121.            } 
  122.        } 
  123.     } 
  124.   
  125.     public static void main(String[] args) { 
  126.        new ChatClientTest("用户" + new Random().nextInt(100)); 
  127.     } 
  128.   
 
七、其他
         客户端记录用的Map,消息就简单定义的字符串,指定某些IP发送消息没写。总之不影响主要功能的,能偷懒的都尽可能偷懒了==。
 
         至于服务器设计上的,挺麻烦。一些开源框架,恩,IBM developerWorks里的一些文章都可以学习借鉴。