群聊內實現私聊功能


首先我們想到的是,消息發過來,我怎么知道是公聊消息還是私聊消息呢。所以,這里需要對消息進行處理,比如說在消息前后都加上一些特殊的字符,我們稱為協議字符。為此,我們可以定義一個接口,專門來定義協議字符。

第二個問題就是,如果是私聊信息,客戶端會將目的用戶(私聊對象)發給服務器端,那么服務器端是如何將找到那個目的用戶的呢。這里,很明顯,我們需要建立一個用戶和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())      {       ps.println(Server.clients.getKeyByValue(this.ps)         +"說:"+Msg);      }     }    }   }   // 捕獲異常,表明該Socket對應的客戶端已出現問題,   // 所以客戶端將其對應的輸出流從Map中刪除   catch (IOException e) {    // TODO Auto-generated catch block    Server.clients.removeByValue(ps);    try{     if(br!=null)     {      br.close();     }     if(ps!=null)     {      ps.close();     }     if(s!=null)     {      s.close();     }    }    catch(IOException ex)    {     ex.printStackTrace();    }   }  }  // 講讀到的內容去掉前后的協議字符,恢復為真實數據  private String getRealMsg(String content) {   // TODO Auto-generated method stub     return content.substring(CrazyitProtocol.PROTOCOL_LEN,     content.length()-CrazyitProtocol.PROTOCOL_LEN);  } }
import java.io.BufferedReader;
import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; import javax.swing.JOptionPane; public class Client {   private static final int PORT=30000;  private Socket s=null;  private PrintStream ps=null;  private BufferedReader brServer=null; //服務器發送過來的內容  private BufferedReader keyIn=null; // 鍵盤輸入內容  public void init()  {   try   {   s=new Socket("127.0.0.1",PORT);   ps=new PrintStream(s.getOutputStream());   keyIn=new BufferedReader(new InputStreamReader(System.in));   brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));     // 用於在服務器端登錄,因為名字有可能重復   String tip="";   while(true)   {    String userName=JOptionPane.showInputDialog(tip      +"輸入用戶名");       // 在用戶輸入的用戶名前后增加協議字符串后發送    ps.println(CrazyitProtocol.USR_ROUND+userName      +CrazyitProtocol.USR_ROUND);    String result=brServer.readLine();    if(result.equals(CrazyitProtocol.NAME_REP))    {     tip="用戶名重復,請重新";     continue;    }    // 登錄成功    if(result.equals(CrazyitProtocol.LOGIN_SUCCESS))    {     break;    }   }   }   catch(UnknownHostException ex)   {    System.out.println("找不到遠程服務器,請確定服務器已啟動!");    closeRs();    System.exit(1);   }   catch(IOException ex)   {    System.out.println("網絡異常!請重新登錄!");    closeRs();    System.exit(1);   }     new Thread(new ClientThread(brServer)); // 子線程負責接收服務器端傳過來的消息    }   private void closeRs() {   // TODO Auto-generated method stub   try{   if(keyIn!=null)   {    keyIn.close();   }   if(brServer!=null)   {    brServer.close();   }     if(ps!=null)   {    ps.close();   }     if(s!=null)   {    s.close();   }   }   catch(IOException e)   {    e.printStackTrace();   }    }   // 主線程的接收鍵盤消息函數  public void readAndSend()  {   String content=null;   try   {   while((content=keyIn.readLine())!=null)   {    // 所發消息中以/開頭,且有:則認為是是私聊信息    if(content.startsWith("/")&&content.indexOf(":")>0)    {     content=content.substring(1); //消息中不需要帶開頭的/     content=CrazyitProtocol.PRAVITE_ROUND+content.split(":")[0]+CrazyitProtocol.SPLIT_SIGN       +content.split(":")[1]+CrazyitProtocol.PRAVITE_ROUND;     ps.println(content);        }    else // 群聊信息    {     content=CrazyitProtocol.MSG_ROUND+content+CrazyitProtocol.MSG_ROUND;     ps.println(content);    }   }      }   catch(IOException e)   {    System.out.println("網絡通信異常!請重新登錄!");    closeRs();    System.exit(1);   }  }   public static void main(String[] args) {   // TODO Auto-generated method stub   Client client=new Client();   client.init();   client.readAndSend();  } }
import java.io.BufferedReader;
import java.io.IOException; public class ClientThread implements Runnable {   private BufferedReader brServer=null;  public ClientThread(BufferedReader brServer)  {   this.brServer=brServer;  }  @Override  public void run() {   // TODO Auto-generated method stub     String content=null;     try   {   while((content=brServer.readLine())!=null)   {    System.out.println(content);   }   }   catch(IOException e)   {    e.printStackTrace();   }   finally   {    try{    if(brServer!=null)    {     brServer.close();    }    }    catch(IOException e)    {     e.printStackTrace();    }   }  } }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM