首先我們想到的是,消息發過來,我怎么知道是公聊消息還是私聊消息呢。所以,這里需要對消息進行處理,比如說在消息前后都加上一些特殊的字符,我們稱為協議字符。為此,我們可以定義一個接口,專門來定義協議字符。
第二個問題就是,如果是私聊信息,客戶端會將目的用戶(私聊對象)發給服務器端,那么服務器端是如何將找到那個目的用戶的呢。這里,很明顯,我們需要建立一個用戶和Socket的映射關系,所以我們采用了map,但是這里的map我們需要改進一下,因為其實我們這里不僅僅是key不能重復,而且value也不能重復,我們也需要通過value能夠查找到key,所以我們進行了改進。
還有一點針對本實現需要指出的是,服務器子線程負責接收和發送消息,這里面也包括客戶端首次建立連接的時候,需要判斷用戶名是否重復,也就是要保證key不重復,於此想對應的,客戶端在首次建立連接時,其需要進行不斷的嘗試,直到提供的名字不重復為止。
代碼如下:
public interface CrazyitProtocol { public static final int PROTOCOL_LEN=2; //默認的類型就是public static final,不加也是可以的 public static final String MSG_ROUND="△▽"; public static final String USR_ROUND="□☆"; public static final String LOGIN_SUCCESS="☆▷"; public static final String NAME_REP="-1"; public static final String PRAVITE_ROUND="◆★"; public static final String SPLIT_SIGN="☀"; }
import java.util.HashMap;
import java.util.HashSet; import java.util.Set; public class CrazyitMap<K,V> extends HashMap<K,V> { // 根據value來刪除指定項 public void removeByValue(Object value) { for(Object key :keySet()) { if(get(key)==value||get(key).equals(value)) { remove(key); break; } } } // 獲取value集合 public Set<V> valueSet() { Set<V> result=new HashSet<V>(); for(Object key : keySet()) { result.add(get(key)); } return result; } // 重寫HashMap的put方法,該方法不允許value重復 public V put(K key,V value) { for(V val : valueSet()) { if(val==value||val.equals(value)) { throw new RuntimeException("MyMap實例中不允許有重復value"); } } return super.put(key, value); } // 通過value查找key public K getKeyByValue(Object value) { for(K key : keySet()) { if(get(key)==value||get(key).equals(value)) { return key; } } return null; } }
import java.io.IOException;
import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class Server { private static final int PORT=30000; public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>(); void init() { try ( ServerSocket ss=new ServerSocket(PORT); ) { while(true) { Socket s=ss.accept(); new Thread(new ServerThread(s)).start(); } }catch (IOException e) { // TODO Auto-generated catch block System.out.println("服務器啟動失敗,是否端口被占用?"); } } public static void main(String[] args) { // TODO Auto-generated method stub Server s=new Server(); s.init(); } }
import java.io.BufferedReader;
import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; public class ServerThread implements Runnable { private Socket s; private BufferedReader br=null; private PrintStream ps=null; public ServerThread(Socket s) { this.s=s; } @Override public void run() { // TODO Auto-generated method stub try { br=new BufferedReader(new InputStreamReader(s.getInputStream())); ps=new PrintStream(s.getOutputStream()); String content=null; while((content=br.readLine())!=null) { if(content.startsWith(CrazyitProtocol.USR_ROUND) //發過來的是名字信息 &&content.startsWith(CrazyitProtocol.USR_ROUND)) { String userName=getRealMsg(content); if(Server.clients.containsKey(userName)) // 姓名重復 { System.out.println("重復"); ps.println(CrazyitProtocol.NAME_REP); } else // 姓名不重復 { System.out.println("成功"); Server.clients.put(userName, ps); ps.println(CrazyitProtocol.LOGIN_SUCCESS); } } else if(content.startsWith(CrazyitProtocol.PRAVITE_ROUND) &&content.startsWith(CrazyitProtocol.PRAVITE_ROUND))// 發過來的是實際的消息,且為私聊消息 { String userAndMsg=getRealMsg(content); String userName=userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0]; String Msg=userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1]; // 獲取私聊用戶的輸出流 Server.clients.get(userName).println(Server.clients.getKeyByValue(ps) +"悄悄的對你說"+Msg); } else // 公聊信息 { String Msg=getRealMsg(content); for(PrintStream ps : Server.clients.valueSet())