一個簡單的基于socket的通訊處理Java程序

jopen 11年前發布 | 23K 次閱讀 Java開發 Socket

這幾天看書看得java網絡編程,看到一個不錯的,適合新手的,部分代碼借鑒書上的,可能有地方還不是很成熟,不過可以借鑒一下,分為客戶端和服務端,話不多說,貼代碼,很多都在注釋中給出

客戶端程序:

YeekuProtocol類,是一個幫助類,用來定義協議字符串   
 /*
 
 *<description>用來定義的協議字符</description>

  • @author Administrator
  • @date 2013-11-26
  • @file CrazyitProtocol.java
  • @category com.inspur.net.MultiThread.chat.server
  • @version 1.0 / public interface YeekuProtocol {

    //定義協議字符串長度為2 int PROROCLO_LEN = 2; //下面是一些協議字符串,服務器和客戶端交換的信息 //都應該在前、后添加這種特殊字符串。 String MSG_ROUND = "§γ"; String USER_ROUND = "∏∑"; String LOGIN_SUCCESS = "1"; String NAME_REP = "-1"; String PRIVATE_ROUND = "★【"; String SPLIT_SIGN = "※"; }

clientThread線程類是客戶端處理輸入流的幾個類

import java.io.BufferedReader; import java.io.IOException;

/* <description>客戶端增加了讓用戶輸入用戶名的代碼,并且不允許用戶名重復 *除此之外,還可以根據用戶的鍵盤輸入來判斷用戶是否想發送私聊信息</description>

  • @author Administrator
  • @date 2013-11-26
  • @file clientThread.java
  • @category com.inspur.net.MultiThread.chat.client
  • @version 1.0 / public class clientThread extends Thread{

    //該線程負責處理輸入流 BufferedReader br = null; public clientThread(BufferedReader br){ this.br = br; } public void run(){ //不斷從輸入流讀取數據,并且打印輸出 String line = null; try { while((line=br.readLine())!=null){ System.out.println(line);

    } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(br!=null){ try { br.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }

client類,主要負責客戶端的一些操作

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;

import com.inspur.net.MultiThread.chat.interfacefolder.YeekuProtocol;

/* <description>客戶端增加了讓用戶輸入用戶名的代碼,并且不允許用戶名重復 *除此之外,還可以根據用戶的鍵盤輸入來判斷用戶是否想發送私聊信息</description>

  • @author Administrator
  • @date 2013-11-26
  • @file client.java
  • @category com.inspur.net.MultiThread.chat.client
  • @version 1.0 / public class Client {

    private static final int SERVER_PORT = 40001; private static final String ip = "192.168.1.123"; private Socket socket; private PrintStream ps ; private BufferedReader brServer; private BufferedReader keyIn;

    public void init(){ keyIn = new BufferedReader(new InputStreamReader(System.in)); try { socket = new Socket(ip,SERVER_PORT); ps = new PrintStream(socket.getOutputStream()); brServer = new BufferedReader(new InputStreamReader(socket.getInputStream())); String tip = ""; //采用循環不斷彈出對話框要求輸入用戶名 while(true){ String userName = JOptionPane.showInputDialog(tip+"輸入用戶名"); //將用戶名前后增加協議字符串 ps.println(YeekuProtocol.USER_ROUND+userName+YeekuProtocol.USER_ROUND); //讀取服務器的相應 String read = brServer.readLine(); //如果用戶重復,開始下一次循環 if(read.equals(YeekuProtocol.NAME_REP)){ tip = "用戶名重復,請刷新"; continue; } //如果服務器返回成功,結束循環 if(read.equals(YeekuProtocol.LOGIN_SUCCESS)){ break; } } } catch (UnknownHostException e) { // TODO Auto-generated catch block System.out.println("找不到遠程服務器,確認服務已經啟動"); closeRs(); System.exit(1); e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block System.out.println("網絡異常,請重新登錄"); e.printStackTrace(); } new Thread(new clientThread(brServer)).run(); } //定義一個從鍵盤讀入,并向網絡發送的方法 public void readAndSend(){ //不斷從鍵盤讀入 String line = null; try { while((line=keyIn.readLine())!=null){ if(line.indexOf(":")>0 && line.startsWith("http://")){//如果以//開頭,則默認為私聊信息,后面必須輸入要私聊的對象 line = line.substring(2); ps.println(YeekuProtocol.PRIVATE_ROUND+line.split(":")[0]+YeekuProtocol.SPLIT_SIGN

    +line.split(":")[1]+YeekuProtocol.PRIVATE_ROUND);
    

    } else{ ps.println(YeekuProtocol.MSG_ROUND+line+YeekuProtocol.MSG_ROUND); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }

    } public void closeRs(){//關閉釋放IO資源 try { if(keyIn!=null){ keyIn.close(); } if(brServer!=null){ brServer.close(); } if(ps!=null){ ps.close(); } if(socket!=null){ socket.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) {//程序入口 Client client = new Client(); client.init(); client.readAndSend(); } }

服務器端:服務器端主要有YeekuMap類和server,serverThred類

YeekuMap類是服務端用來存儲用戶名和輸入輸出流之間的映射關系,這樣服務端就可以根據用戶名來尋找相對應的socket,實現私密通信,利用map這一數據結構存儲

import java.util.HashMap; import java.util.HashSet; import java.util.Set; /* <description>服務器端用來存儲用戶名和輸入輸出流之間的映射關系,這樣服務器就可以根據用戶名來 尋找相應的socket,實現客戶端之間的通信,服務器只要獲取gia用戶名對應的輸出流即可,提供的這個HashMap類 *可以根據value獲取key值,或者是通過value刪除key值,而且不允許value的重復</description>

  • @author Administrator
  • @date 2013-11-26
  • @file YeekuMap.java
  • @category com.inspur.net.MultiThread.chat.server
  • @version 1.0 / public class YeekuMap<K,V> extends HashMap<K,V>{

    /* */ private static final long serialVersionUID = 1L; //根據value值來刪除指定項 public void remonveByValue(Object value){ for(Object key : keySet()){ if(get(key)==value){ remove(key); break; } } } //獲取所有value組成的集合 public Set<V> valueSet(){ Set<V> result = new HashSet<V>(); for(K key : keySet()){ //將每個key對應的value添加到result集合中 result.add(get(key)); } return result; } //通過value查找key public K getKeyByValue(V val){ //便利所有key組成的集合 for(K key : keySet()){ //如果指定key對應的value與被搜索的value相同 //則返回該key的值 if(get(key).equals(val) &&get(key)==val){ return key; }

    } return null; } //重寫HashMap的put方法,該方法允許value的重復 public V put(K key , V value){ for(V val : valueSet()){ //如果指定的value與試圖放入集合的value相同 //則拋出一個RuntimeException if(val == get(key) && val.equals(get(key))){ throw new RuntimeException("MyMap實例中不允許重復"); } } return super.put(key, value); }

}

server類,

import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket;

/* *<description>服務器端程序,建立ServerSocket監聽客戶端,增加異常處理</description>

  • @author Administrator
  • @date 2013-11-26
  • @file Server.java
  • @category com.inspur.net.MultiThread.chat.server
  • @version 1.0 / public class Server {

    //監聽端口 private static final int SERVER_PORT = 40001; //使用MyMap對象來存儲每個客戶名字和對應的輸出流之間的關系 public static YeekuMap<String ,PrintStream> clients = new YeekuMap<String,PrintStream>(); private Socket socket; public void init(){ try { ServerSocket serverSocket = new ServerSocket(SERVER_PORT); while(true){ socket = serverSocket.accept(); new Thread(new ServerThread(socket)).start(); } } catch (IOException e) { // TODO Auto-generated catch block System.out.println("服務器已經啟動,是否端口"+SERVER_PORT+"被占用"); e.printStackTrace(); }finally{ if(socket!=null){ try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.exit(1); } } } public static void main(String[] args) { Server server = new Server(); server.init(); } }

serverThraead類

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket;

import com.inspur.net.MultiThread.chat.interfacefolder.YeekuProtocol; /* *<description>服務器端程序,處理客戶端請求的實體類</description>

  • @author Administrator
  • @date 2013-11-26
  • @file ServerThread.java
  • @category com.inspur.net.MultiThread.chat.server
  • @version 1.0 / public class ServerThread implements Runnable{

    private Socket socket; private BufferedReader br = null; private PrintStream ps = null; public ServerThread(Socket socket){ this.socket = socket; try { br = new BufferedReader(new InputStreamReader(socket.getInputStream())); ps = new PrintStream(socket.getOutputStream()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { // TODO Auto-generated method stub String line = null; try { while((line=br.readLine())!=null){ //如果讀到以MyProtocol.USER ROUND開始和結束 //可以確定讀到的是用戶的用戶名 if(line.startsWith(YeekuProtocol.USER_ROUND) && line.endsWith(YeekuProtocol.USER_ROUND)){

    String userName = getRealMsg(line); if(Server.clients.containsKey(userName)){ System.out.println("重復"); ps.println(YeekuProtocol.NAME_REP); }else{ System.out.println("成功"); ps.println(YeekuProtocol.LOGIN_SUCCESS); Server.clients.put(userName, ps); } } //如果讀到的行以YeekuProtocol.PRIVATE_ROUND開始結束 //可以確定是私聊信息,私聊信息都以特定的流輸出 else if(line.startsWith(YeekuProtocol.PRIVATE_ROUND) && line.endsWith(YeekuProtocol.PRIVATE_ROUND)){ //得到真實信息 String userAndMsg = getRealMsg(line); String User = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[0]; String Msg = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[1]; //獲取私聊用戶的輸出流,并發送私聊信息 Server.clients.get(User).println(

    Server.clients.getKeyByValue(ps)+"悄悄對你說:"+Msg);
    

    } //公聊的話向每個socket都發送 else{ //得到真實信息 String msg = getRealMsg(line); //便利clients的每個輸出流 for(PrintStream clientsPs : Server.clients.valueSet()){ clientsPs.println(Server.clients.getKeyByValue(ps)+"說"+msg); } } } } catch (IOException e) { // TODO Auto-generated catch block Server.clients.remonveByValue(ps); System.out.println(Server.clients.size()); //關閉io資源 try { if(br != null){ br.close(); } if(ps != null){ ps.close(); } if(socket !=null){ socket.close(); } } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } } //將讀到的內容去掉前后的字符串,恢復成真實數據 public String getRealMsg(String line){ return line.substring(YeekuProtocol.PROROCLO_LEN, line.length()-YeekuProtocol.PROROCLO_LEN); } } </pre>

    以上就是這個簡單實現,我測試的時候時將他們導出成jar文件,然后再cmd窗口運行jar文件,此處在

    MANIFEST.MF文件中應該加上一句Main-Class: 包名+類名,注意:后面必須有個空格,而且在結束之后要以換行結束,shift+enter

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!