2013-02-19 51 views
2

我想有此設置客戶端服務器通信:其中Server啓動

  • 服務器託管TCP套接字服務器連接在TCP
  • 多個客戶端(保持連接打開)

然後我想要從服務器向客戶端發起消息。我無法弄清楚如何做到這一點,並且同時有多個客戶端會話。我讀過的技術涉及服務器在端口上偵聽,並且當它從客戶端接收到通信時,它會啓動一個新線程來處理和處理該線程,然後返回監聽端口以查看另一客戶端的下一個請求。
那麼,我將如何利用它併發送消息給在其中一個線程上運行的客戶端?

如果您有興趣,我的實際使用情況如下。最終目標就像您的文件系統上傳文件到服務器的遠程控制。 - 每個客戶端都有一個運行在系統托盤中的java後臺應用程序,該應用程序連接到服務器 - 服務器主機連接,並託管RESTFul Web服務以啓動通信。 - 移動設備通過RESTFul web服務連接到服務器以請求關於客戶端文件系統。因此它可以下鑽並找到一個文件,然後單擊並將文件上傳到服務器。 這裏的想法是移動用戶需要將文件從他們的桌面上傳到服務器,而不需要在辦公室使用移動設備。 (這是定製產品,所以不能使用第三方APP_

PS:我一直在尋找簡單的客戶端 - 服務器的聊天程序在這裏:http://way2java.com/networking/chat-program-two-way-communication/

回答

0

你想擁有一臺服務器一旦服務器發現該端口上的傳入連接,你應該創建一個新的線程來處理該客戶端和服務器之間的通信,而主線程繼續監聽其他傳入的連接。您可以將多個客戶端連接到一臺服務器,如下所示:

private void listen() throws IOException { 
    serverSocket = new ServerSocket(port) 
    while (GlobalFlags.listening) { 
     new ServerThread(serverSocket.accept(); 
     if (GlobalFlags.exit) { 
      serverSocket.close(); 
      break; 
     } 
    } 
} 

Where Global標誌是控制監聽過程的變量,並非真正必要。你可以做一段時間的真實,並永遠不停地傾聽。

在我的項目中,我有一個主監聽器在線程中運行的服務器控制器。控制器控制GlobalFlags。我敢肯定,不是使用全局標誌,而是進行線程間通信的更好方法,但對我來說這是最簡單的。

ServerThread應該循環所有的時間在發送輸出到客戶端和接收來自客戶端的輸入之間切換。像這樣:

ServerThread(Socket socket) { 
    super("GameServerThread"); 
    this.socket = socket; 
    try { 
     this.socket.setTcpNoDelay(true); 
    } catch (SocketException e) { 
     // Error handling 
    } 
    this.terminate = false; 
} 

@Override 
public void run() {   
    try { 
     out = new PrintWriter(socket.getOutputStream(), true); 
     in = new BufferedReader(
      new InputStreamReader(
      socket.getInputStream())); 

     String inputLine, outputLine; 

     while ((inputLine = in.readLine()) != null) { 
       outputLine = processInput(inputLine); 
       out.println(outputLine); 
       if (terminate) { 
        break; 
       } 
      } 
     } 
     out.close(); 
     in.close(); 
     socket.close(); 

    } catch (Exception e) { 
     // Error handling, should not use Exception but handle all exceptions by themselves. 
} 

在客戶端,您有一個線程運行通過類似的循環,從服務器接收輸入,然後將輸出發送到服務器。

在此示例中,processInput是用於處理客戶端輸入的函數。如果你想讓服務器發起聯繫,你可以讓服務器在輸出流中發送一些東西,然後再聽取輸入並讓客戶端先聽。

我已經從我自己的一個項目中提取了這個示例,並且this.socket.setTcpNoDelay(true)應該使該過程更快。當http://www.rgagnon.com/javadetails/java-0294.html

「java.net.Socket.setTcpNoDelay()被用於使能/禁用TCP_NODELAY其中禁用/啓用Nagle算法 Nagle算法嘗試通過最小化所發送的段的數量,以節省帶寬:參考這裏。應用程序希望減少網絡延遲並提高性能,他們可以禁用Nagle的算法(即啓用TCP_NODELAY)。數據將以更高的帶寬消耗爲代價提前發送.Nagle的算法在RFC 896中描述。

用java.net.Socket.getTcpNoDelay()獲得當前的「TCP_NODELAY」設置「


因此,要將消息發送到特定的客戶端,您可以將創建時的所有線程置於ArrayList中,以便跟蹤所有當前連接的客戶端。您可以讓processInput方法暫停和輪詢隊列/變量,直到另一個類將消息發送到隊列/變量中。那麼如何獲得類的句柄取決於你的processInput的實現。你可以給每個線程一個ID(這是我在我的項目中做的),也可以讓processInput方法在index = ID處輪詢一個ArrayList。然後發送輸出到客戶端,你將不得不在index = ID處設置變量。

這種方法對我個人而言似乎有點笨重,但我不確定我會怎麼做。您可能會使用隊列並讓processInput將輸入寫入其隊列,然後等待另一個類讀取它並將其響應放入隊列中。但我個人從來沒有在java中使用Queues,所以你應該自己閱讀。

+0

謝謝。我跟着你,直到服務器控制器部分。這是我感到困惑的地方。所以一旦你有這些客戶端通過自己的線程與服務器進行通信,你如何從外部java類獲得一個句柄來向該客戶端發送消息。 因此可以說我有客戶端1在線程XYZ中運行。然後在另一個java類中處理RESTful webservice想要在Thread XYZ上向客戶端1發送消息。它如何獲得該線程的句柄? – Goldcougar 2013-02-19 15:19:44

+0

那麼服務器控制器是非常不相關的問題。所有你真正需要的功能是監聽器是服務器線程和客戶端線程。將消息發送到客戶端的方式將通過您的processInput實現來確定。我個人在另一個類中有一個解碼函數,它與服務器後端的其餘部分進行通信,以根據客戶端的輸入來確定服務器的輸出。我將在您編輯的同時編輯更多信息的帖子。 – 2013-02-19 15:24:23

+0

好吧,我已經添加了一些更多的信息,我會給你一個示例實現,但我自己不太確定什麼是最好的方法。你或許可以考慮一下使用隊列的方法。 – 2013-02-19 15:45:03

0

在我的知識 1)服務器託管TCP套接字服務器 - 可能 2)連接通過TCP多臺客戶機 - 可能 3),那麼我想從服務器到客戶端發起的消息 - 不可能。客戶端必須發起連接創建,然後服務器可能能夠向您發送數據包。例如:您需要在您的瀏覽器上打開Facebook網站,Facebook服務器無法自行決定將其頁面發送至您的PC,因爲您的PC不會有靜態IP地址,並且如果Facebook假設編寫代碼以啓動與PC的連接,那麼它就像你的電腦是服務器和Facebook網站/服務器充當客戶端一樣好。