四、Message.java
定義的字符串消息。
- public class Message {
- static class MessageHolder {
- static Message instance = new Message();
- }
- public static Message getInstance() {
- return MessageHolder.instance;
- }
- public static final String SEPARATOR = "`";
- /** 客戶端登錄服務器的消息類型,用以通知登錄名等 */
- public static final int MSG_1 = 1; // 1`msg
- /** 服務器通知各客戶端新登錄者信息的消息類型 */
- public static final int MSG_2 = 2; // 2`msg`fromIp
- /** 服務器回覆新登錄者所有在線客戶端信息的消息類型 */
- public static final int MSG_3 = 3; // 3`ip`msg`ip`msg`...
- /** 客戶端發送所有人信息的消息類型(或服務器發送消息) */
- public static final int MSG_4 = 4; // 4`msg
- /** 客戶端發送指定人信息的消息類型 */
- public static final int MSG_5 = 5; // 5`msg`toIp`toIp`...
- /** 服務器轉發發送者信息的消息類型 */
- public static final int MSG_6 = 6; // 6`msg`fromIp
- /** 客戶端通知服務器退出的消息類型 */
- public static final int MSG_7 = 7; // 7
- /** 服務器通知各客戶端退出者信息的消息類型 */
- public static final int MSG_8 = 8; // 8`fromIp
- // 構造和解析方式詳細參見附件工程了
- }
五、ChatServerTest.java
服務器消息處理實現。
- public class ChatServerTest implements Observer, Runnable {
- private static final String BLANK = " ";
- /** 在線客戶端信息 */
- private HashMap<InetSocketAddress, String> onLineMap;
- private ChatServer server;
- public ChatServerTest() {
- server = new ChatServer(8899);
- server.addObserver(this);
- server.start();
- new Thread(this).start();
- }
- @Override
- public void update(Observable o, Object arg) {
- ChatServer server = (ChatServer) o;
- switch (server.getStatus()) {
- case ChatServer.SEV_ON:
- System.out.println("服務器開啓了");
- onLineMap = new HashMap<InetSocketAddress, String>();
- break;
- case ChatServer.SEV_OFF:
- System.out.println("服務器關閉了");
- onLineMap = null;
- break;
- case ChatServer.CLT_CONNECT:
- // InetSocketAddress address = (InetSocketAddress) arg;
- break;
- case ChatServer.CLT_DISCONNECT:
- quit((InetSocketAddress) arg);
- break;
- case ChatServer.MSG_SEND:
- // System.out.println("通知:"
- // + ((String) arg).split(Message.SEPARATOR)[1]);
- break;
- case ChatServer.MSG_RECEIVE:
- Message msg = Message.getInstance();
- msg.create(server.getReceiveMessage());
- msg.setFromIp((InetSocketAddress) arg);
- handleMsg(msg); // 處理消息
- break;
- case ChatServer.ERROR:
- System.out.println("error : " + ((Exception) arg).getMessage());
- break;
- }
- }
- private void handleMsg(Message msg) {
- int type = msg.getType();
- InetSocketAddress formIp = msg.getFromIp();
- switch (type) {
- case Message.MSG_1:
- System.out.println(msg.getMsg() + BLANK + msg.toIpString(formIp)
- + BLANK + "登錄了");
- onLineMap.put(formIp, msg.getMsg());
- /* 通知所有客戶端新登錄者信息 */
- msg.setType(Message.MSG_2);
- // msg.setFromIp(address);
- server.send(msg.toString());
- /* 返回登錄客戶端所有在線客戶端信息(會覆蓋前一個發送給address的信息) */
- msg.setType(Message.MSG_3);
- msg.setOnLineMap(onLineMap);
- server.send(msg.toString(), formIp);
- break;
- case Message.MSG_4:
- System.out.println(onLineMap.get(msg.getFromIp()) + BLANK + "said"
- + BLANK + msg.getMsg());
- msg.setType(Message.MSG_6);
- server.send(msg.toString());
- break;
- case Message.MSG_7:
- quit(formIp);
- break;
- }
- }
- private void quit(InetSocketAddress address) {
- if (onLineMap.get(address) != null) {
- System.out
- .println(onLineMap.get(address) + BLANK
- + Message.getInstance().toIpString(address) + BLANK
- + "退出了");
- onLineMap.remove(address);
- Message msg = Message.getInstance();
- msg.setType(Message.MSG_8);
- msg.setFromIp(address);
- server.send(msg.toString());
- }
- }
- @Override
- public void run() {
- BufferedReader br = null;
- try {
- br = new BufferedReader(new InputStreamReader(System.in));
- while (true) {
- String inputLine = br.readLine();
- if (inputLine.trim().toLowerCase().equals("off")) {
- server.close();
- break;
- } else {
- // 鍵盤輸入爲GBK編碼,需要轉成UTF-8纔可顯示。或者變爲GBK工程。
- // 這麼轉碼仍是有問題的。例如奇數漢字“大家好”時,顯示爲“大家�?”。可以看下如下網址:
- // http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html
- inputLine = new String(inputLine.getBytes("gbk"), "utf-8");
- server.send(Message.MSG_4 + Message.SEPARATOR + inputLine);
- System.out.println("通知:" + inputLine);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (br != null) {
- br.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public static void main(String[] args) {
- new ChatServerTest();
- }
- }
六、ChatClientTest.java
客戶端消息處理實現。
- public class ChatClientTest implements Observer, Runnable {
- private static final String BLANK = " ";
- /** 在線客戶端信息 */
- private HashMap<InetSocketAddress, String> onLineMap;
- private String name;
- private ChatClient client;
- public ChatClientTest(String name) {
- this.onLineMap = new HashMap<InetSocketAddress, String>();
- this.name = name;
- client = new ChatClient("192.168.1.107", 8899);
- // client = new ChatClient("127.0.0.1", 8899);
- client.addObserver(this);
- client.start();
- new Thread(this).start();
- }
- @Override
- public void update(Observable o, Object arg) {
- ChatClient client = (ChatClient) o;
- switch (client.getStatus()) {
- case ChatClient.CLT_CONNECT:
- // 發送登錄消息
- Message msg1 = Message.getInstance();
- msg1.setType(Message.MSG_1);
- msg1.setMsg(name);
- client.send(msg1.toString());
- break;
- case ChatClient.CLT_DISCONNECT:
- System.out.println("斷開服務器連接");
- System.exit(1);
- break;
- case ChatClient.MSG_SEND:
- break;
- case ChatClient.MSG_RECEIVE:
- Message msg = Message.getInstance();
- msg.create((String) arg);
- handleMsg(msg); // 處理消息
- break;
- case ChatClient.ERROR:
- System.out.println("error : " + ((Exception) arg).getMessage());
- break;
- }
- }
- private void handleMsg(Message msg) {
- int type = msg.getType();
- switch (type) {
- case Message.MSG_2:
- System.out.println(msg.getMsg() + BLANK
- + msg.toIpString(msg.getFromIp()) + BLANK + "登錄了");
- onLineMap.put(msg.getFromIp(), msg.getMsg());
- break;
- case Message.MSG_3:
- onLineMap = msg.getOnLineMap();
- System.out.println("連接上了服務器");
- System.out.println("/***在線成員***/");
- Iterator<Entry<InetSocketAddress, String>> it = onLineMap
- .entrySet().iterator();
- while (it.hasNext()) {
- Entry<InetSocketAddress, String> entry = it.next();
- InetSocketAddress address = entry.getKey();
- System.out.println(entry.getValue() + BLANK
- + address.getAddress().getHostAddress() + ":"
- + address.getPort());
- }
- System.out.println("/***在線成員***/");
- break;
- case Message.MSG_4:
- System.out.println("通知:" + msg.getMsg());
- break;
- case Message.MSG_6:
- System.out.println(onLineMap.get(msg.getFromIp()) + BLANK + "said"
- + BLANK + msg.getMsg());
- break;
- case Message.MSG_8:
- InetSocketAddress address = msg.getFromIp();
- System.out
- .println(onLineMap.get(address) + BLANK
- + Message.getInstance().toIpString(address) + BLANK
- + "退出了");
- onLineMap.remove(address);
- break;
- }
- }
- @Override
- public void run() {
- BufferedReader br = null;
- try {
- br = new BufferedReader(new InputStreamReader(System.in));
- while (true) {
- String inputLine = br.readLine();
- if (inputLine.trim().toLowerCase().equals("quit")) {
- client.send(Message.MSG_7 + "");
- client.close();
- break;
- } else {
- // 參見ChatServerTest註釋
- inputLine = new String(inputLine.getBytes("gbk"), "utf-8");
- client.send(Message.MSG_4 + Message.SEPARATOR + inputLine);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (br != null) {
- br.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public static void main(String[] args) {
- new ChatClientTest("用戶" + new Random().nextInt(100));
- }
- }
七、其他
客戶端記錄用的Map,消息就簡單定義的字符串,指定某些IP發送消息沒寫。總之不影響主要功能的,能偷懶的都儘可能偷懶了==。
至於服務器設計上的,挺麻煩。一些開源框架,恩,IBM developerWorks裏的一些文章都可以學習借鑑。