2014-02-11 38 views
0

我開始學習與JDK中主要網絡包的聯網,在幾個示例之後,它非常簡單,容易。但是現在我有興趣創建像聊天系統這樣的多客戶端應用程序。Java從客戶端等待並獲取數據的最佳方式

我的想法結構到目前爲止是這樣的:

連接處理類,它處理傳入的連接,並持有客戶的名單。 如果找到新的連接,創建一個新的客戶端對象,啓動它的線程(Client對象將實現可運行的,因此它將啓動它自己的循環服務,它將循環接收新的數據包),並將其添加到列表中。

我爲每個客戶端創建一個新線程,而不是循環遍歷所有客戶端,因爲從客戶端進程讀取會停止整個執行並等待客戶端發送數據,這有點讓我很惱火,這是我的問題。

我已經創建了一個簡單的控制檯應用程序接收來自客戶端的消息,但現在我想檢測斷開連接。如果用戶沒有連接,我讀bufferedReader .read()方法返回-1,所以我想我可以循環,併爲每個客戶端每隔幾秒鐘執行一次,但事實是,客戶端必須發送一個數據包才能完成。讀()它,讓我們說如果你這樣做.read()它會等待&停止整個線程,直到收到數據包,(我認爲)。

這是我當前的代碼,從客戶端獲得消息:

public boolean isConnected() { 
    try { 
     this.in.read(); 
     this.lastCheck = System.currentTimeMillis(); 
     return true; 
    } catch (IOException e) { 
     if (!inConnection()) { 
      System.out.println("User disconnected"); 
      try { 
       this.destruct(); 
      } catch (IOException e1) { 
       e1.printStackTrace(); 
      } 
     } 
    } 
    return false; 
} 

private boolean inConnection() { 
    return System.currentTimeMillis() - lastCheck < this.maxTime; 
} 

public void startClientService() throws IOException { 
    while(!this.session.isClosed()) { 
     if (System.currentTimeMillis() - this.checkTime > 600) { 
      System.out.println(System.currentTimeMillis() - this.checkTime); 
      if (this.isConnected()) { 
       int packetType = this.dataIn.readInt(); 
       packets.getPacket(packetType); 
      } 
     } 
    } 
} 

public void destruct() throws IOException { 
    this.session.close(); 
    this.connection.removeClient(this); 
    System.out.println("Session killed"); 
} 

基本上這裏發生了什麼,我送從客戶端裝一個整數,我可能有許多事情要做所以因此我可以設置多唯一的數據包ID,所以如果我想接收和處理聊天消息,數據包ID是216,客戶端發送一個int 216,服務器讀取數據包,進入所有數據包ID的交換機循環,並檢測它是否真的是216,如果是的,它得到處理消息的打包類的實例&獲取接收消息的字節,如下所示:

public class Chat implements Packet { 

    @Override 
    public void processPacket(Session c) { 
     String message = readMessage(c); 
     System.out.println("Message: " + message); 
    } 

    private String readMessage(Session c) { 
     byte[] data = c.readBytes(); 
     String message = null; 
     try { 
      message = new String(data, "UTF-8"); 
     } catch (UnsupportedEncodingException e) { 
      e.printStackTrace(); 
     } 
     return message; 
    } 
} 

這就是我如何讀取的字節:

public byte[] readBytes() { 
    int len; 
    byte[] data = null; 
    try { 
     len = this.dataIn.readInt(); 
     data = new byte[len]; 
     if (len > 0) { 
      this.dataIn.readFully(data); 
     } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    }  
    return data; 
} 

好了我的問題:

添加斷開檢測,當我把我的消息後,沒有任何反應。這可能是由於.read()它停止並正在等待響應。但是如果我再次寫一條消息,我會在服務器上收到消息。

這是我暫時的,醜陋的客戶:

public class Client { 

    public static void main(String[] args) throws UnknownHostException, IOException { 
     Socket socket = new Socket("127.0.0.1", 43594); 
     Scanner r = new Scanner(System.in); 
     PrintWriter out = new PrintWriter(socket.getOutputStream()); 
     String input; 
     while(true) { 
      input = r.next(); 
      if (input != null) { 
       sendMessage(input, out); 
      } 
     } 

    } 

    public static void sendMessage(String message, PrintWriter out) { 
     byte[] encoded = encode(message); 
     out.write(0); 
     out.println(encoded + "\n"); 
     out.flush(); 
    } 

    public static byte[] encode(String s) { 
     return DatatypeConverter.parseBase64Binary(s); 
    } 

    public static String decode(byte[] s) { 
     return DatatypeConverter.printBase64Binary(s); 
    } 
} 

我的問題是:什麼是讀取來自客戶端的數據,而不使應用程序等待它實際上循環每次一個更好的辦法?或者也許我應該有一個新的線程來檢查用戶是否在線,因此每個客戶端有2個線程?

如果有人需要我的會話對象(客戶端對象):

public class Session extends Thread implements Runnable { 

    private Socket session; 
    private Client client; 
    private PrintWriter out; 
    private BufferedReader in; 
    private PacketHandler packets; 
    private DataInputStream dataIn; 
    private ConnectionHandler connection; 

    private final int checkTime = 1600; 
    private final int maxTime = 22000; 
    private long lastCheck; 

    public Session(Socket session) { 
     this.session = session; 
     this.client = new Client(this); 
     try { 
      this.setStream(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     this.packets = new PacketHandler(this); 
     System.out.println("[New session created]: " + session.getRemoteSocketAddress()); 
    } 

    public void setConnectionHandler(ConnectionHandler c) { 
     this.connection = c; 
    } 

    public void run() { 
     try { 
      this.startClientService(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    public void setStream() throws IOException { 
     this.out = new PrintWriter(this.session.getOutputStream()); 
     this.in = new BufferedReader(new InputStreamReader(this.session.getInputStream())); 
     this.dataIn = new DataInputStream(this.session.getInputStream()); 
    } 

    public Client getClient() { 
     return this.client; 
    } 

    public byte[] readBytes() { 
     int len; 
     byte[] data = null; 
     try { 
      len = this.dataIn.readInt(); 
      data = new byte[len]; 
      if (len > 0) { 
       this.dataIn.readFully(data); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     }  
     return data; 
    } 

    public String readMessage() { 
     try { 
      return this.in.readLine(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 

    public boolean isConnected() { 
     try { 
      this.in.read(); 
      this.lastCheck = System.currentTimeMillis(); 
      return true; 
     } catch (IOException e) { 
      if (!inConnection()) { 
       System.out.println("User disconnected"); 
       try { 
        this.destruct(); 
       } catch (IOException e1) { 
        e1.printStackTrace(); 
       } 
      } 
     } 
     return false; 
    } 

    private boolean inConnection() { 
     return System.currentTimeMillis() - lastCheck < this.maxTime; 
    } 

    public void startClientService() throws IOException { 
     while(!this.session.isClosed()) { 
      if (System.currentTimeMillis() - this.checkTime > 600) { 
       System.out.println(System.currentTimeMillis() - this.checkTime); 
       if (this.isConnected()) { 
        int packetType = this.dataIn.readInt(); 
        packets.getPacket(packetType); 
       } 
      } 
     } 
    } 

    public void destruct() throws IOException { 
     this.session.close(); 
     this.connection.removeClient(this); 
     System.out.println("Session killed"); 
    } 

} 

謝謝!

+0

您是將此項目編寫爲學習練習還是解決問題? – chrylis

+0

@chrylis學習,但不能真正解決這個問題tbh – Artemkller545

回答

0

雖然我沒有時間查看所有代碼,但有兩件事可以幫助您。

1)使用定義的消息頭。定義客戶端將發送到服務器的每條消息的X個字節數。使用這些字節來定義消息的長度以及消息的類型。服務器知道這個頭的長度和佈局,並用它以特定的方式處理消息。示例可以是一個字節的頭部。值爲1可能是我連接的消息。 2可能是我即將斷開連接。 3可能是我目前離開,並且4可能是傳入的聊天消息。

2)有兩種方法可以處理輸入。首先是使用阻塞IO,並創建一個單獨的線程來接收來自每個客戶端的消息。我相信這是你現在正在做的。第二個是使用非阻塞IO,並有一個單獨的線程迭代打開的套接字並進行讀取。非阻塞將檢查是否有數據要讀取,但如果沒有,線程將繼續執行。

+0

「非阻塞將檢查是否有數據要讀取,但如果沒有,線程將繼續執行。」我怎樣才能做到這一點? – Artemkller545

+0

這裏有很多教程。只要谷歌的「非阻塞IO Java」或沿着這些線路。 – aglassman

相關問題