2011-08-04 88 views
2

在java.net中使用Java TCP/IP庫,是否有一種方法可以實現可靠的通信(發送方得到接收方已發送的消息)?我知道TCP over UDP的優點之一就是它的可靠性。然而,我不能讓這種保證在下面的實驗:Java中的可靠TCP/IP

我創建了兩個類:

1)回聲服務器=>總是發送回它接收到的數據。

2)cli​​ent =>週期性地向echo服務器發送「Hello world」消息。

他們在不同的計算機上運行(並完美地工作)。在執行過程中,我斷開了網絡連接(拔掉了LAN電纜)。斷開連接後,服務器仍然在等待數據,直到數秒後(它最終引發異常)。同樣,客戶端也會一直髮送數據直到數秒過去(引發異常)。

問題是,objectOutputStream.writeObject(message)不保證郵件的傳遞狀態(我期望它阻塞線程,保持重新傳送數據直到交付)。或者至少我得到通知,哪些消息丟失。

服務器代碼:

import java.net.*; 
import java.io.*; 

import java.io.Serializable; 

public class SimpleServer { 
    public static void main(String args[]) { 
     try { 
      ServerSocket serverSocket = new ServerSocket(2002); 
      Socket socket = new Socket(); 
      socket = serverSocket.accept(); 
      InputStream inputStream = socket.getInputStream(); 
      ObjectInputStream objectInputStream = new ObjectInputStream(
        inputStream); 

      while (true) { 
       try { 
        String message = (String) objectInputStream.readObject(); 
        System.out.println(message); 
        Thread.sleep(1000); 
       } catch (Exception ex) { 
        ex.printStackTrace(); 
       } 
      } 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    } 
} 

客戶端代碼:

import java.net.*; 
import java.io.*; 

public class SimpleClient { 
    public static void main(String args[]) { 
     try { 
      String serverIpAddress = "localhost"; //change this 

      Socket socket = new Socket(serverIpAddress, 2002); 
      OutputStream outputStream = socket.getOutputStream(); 
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(
        outputStream); 

      while (true) { 
       String message = "Hello world!"; 
       objectOutputStream.writeObject(message); 

       System.out.println(message); 
       Thread.sleep(1000); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

回答

1

TCP總是可靠的。你不需要確認。但是,要檢查客戶端是否已啓動,您可能還需要使用帶有確認的UDP流。像PING? PONG!系統。也可能是您可以調整的TCP設置。

+1

那麼,如何檢測斷開連接時丟失哪些消息? –

+0

當客戶端連接到服務器時,您可以使用TCP握手,說明上次發送了多少。在TCP傳輸開始之前,您應該傳輸文件大小。這就是HTTP下載的工作方式,您可以在這裏恢復傳輸。客戶也知道他們的轉移並不完整。 –

+0

這是錯誤的,請參閱http://stackoverflow.com/questions/6939204/reliable-tcp-ip-in-java#comment14181788_6939281 – schlamar

1

你的基本假設(以及對TCP的理解)是錯誤的。如果拔下插頭然後重新插入,則最有可能的信息不會丟失。
它可以歸結爲您希望發件人等待多久。一個小時,一天?如果你有一天會超時,你會拔掉兩天,仍然說「不起作用」。

所以有保證的交付是「要麼交付數據 - 要麼得到通知」。在第二種情況下,您需要在應用程序級別解決它。

+0

我試圖拔掉並replug,但在該期間發送的消息不被服務器接收。 –

+0

這是不正確的保證交付,請參閱:http://lkml.indiana.edu/hypermail/linux/kernel/0106.1/1154.html – schlamar

1

您可以考慮使用SO_KEEPALIVE套接字選項,如果沒有數據通過套接字傳輸2個小時,將導致連接關閉。然而,顯然在很多情況下,這並不能提供應用程序通常需要的控制級別。

第二個問題是某些TCP/IP協議棧實現很差,在網絡中斷的情況下可能會使服務器處於懸空的連接狀態。

因此,我建議在您的客戶端和服務器之間添加應用程序級別的心跳,以確保雙方都還活着。這也提供了切斷連接的優點,例如,如果第三方客戶端仍然活着但變得沒有響應並因此停止發送心跳。

3

如果您需要知道哪些消息已到達對等應用程序,則對等應用程序必須發送確認。

+0

它不是已經完成了TCP協議? –

+0

@Pahlevi Fikri Auliya不,它不是。 TCP/IP盡最大努力確保到達對等主機。如果你想知道它是否進入對等應用程序,對等應用程序必須告訴你。 – EJP

+0

+1如果你不想使用RMI或MOM等更高級別的協議,這是最好的選擇。 – schlamar

3

如果你想要這個級別的保證,它聽起來像你真的想要JMS。這可以確保不僅消息已經傳遞,而且已經被正確處理。即如果由於錯誤而丟棄它,那麼沒有可靠的交付。

您可以監視哪些消息正在等待以及哪些消費者落後。觀察製作人,看看它發送的是什麼信息,並在信息關閉時保存信息並在重新啓動時可用。即使消費者重新啓動也是可靠的交付。