2011-06-04 117 views
6

我正在用Java開發一個小型客戶端 - 服務器程序。Java - 網絡 - 最佳實踐 - 混合同步/異步命令

客戶端和服務器通過一個tcp連接進行連接。通信的大部分部分是異步的(可以隨時發生),但我希望某些部分是同步的(比如發送命令的ACK)。

我使用一個線程從套接字的InputStream中讀取命令並引發一個onCommand()事件。命令本身是由命令設計模式推進的。

什麼是最佳實踐方法(Java),以便能夠等待ACK而不會錯過可能同時出現的其他命令?

con.sendPacket(new Packet("ABC")); 
// wait for ABC_ACK 

EDIT1

它認爲像一個FTP-連接,但兩者的數據和控制命令是在同一連接上。我想捕獲對控制命令的響應,而後臺的數據流正在運行。

EDIT2

一切都在塊發送到啓用多個(不同的)在相同的TCP連接被transmissons(複用)

Block: 
1 byte - block's type 
2 byte - block's payload length 
n byte - block's paylod 
+0

所以在發送數據包之後,您希望方法能夠**阻塞**直到它收到ACK數據包? – khellang 2011-06-04 21:23:36

+0

你想發送「ABC」並等待確認,但是也可以發送「EFG」而不必因ABC_ACK而延遲? – Cratylus 2011-06-04 21:36:30

+0

對兩者都是。我有同一連接上的控制命令(如ABC)和數據流。閱讀線程讀取它們兩個。所以我的問題是,如何將ABC_ACK從讀線程「傳遞」到發送ABC的(阻塞)方法。 – kazu 2011-06-04 22:30:51

回答

4

原則上,需要阻塞的線程的一個註冊表(或更好,他們正在等待的鎖),用一些將由遠程端發送的標識符鍵入。

對於異步操作,您只需發送消息並繼續。

對於同步操作,在發送消息之後,發送線程(或啓動此線程的線程)創建一個鎖定對象,並在註冊表中添加一些關鍵字,然後等待鎖定直到通知。

閱讀線程在收到某個答案時,在鎖定對象的註冊表中查找,爲其添加答案,並調用notify()。然後它讀取下一個輸入。

這裏的努力工作是正確的同步,以避免死鎖以及缺少通知(因爲它會在我們將自己添加到註冊表之前回來)。

當我爲我們的Fencing -applet實現遠程方法調用協議時,我做了這樣的事情。原則上RMI的工作方式是相同的,只是沒有異步消息。

+0

所以我會這樣做:'reader.waitForCmd(「ABC_ACK」)'內部添加一些東西到列表中,等待並通知(恢復)當ABC_ACK通過閱讀線程到達? – kazu 2011-06-05 08:29:18

+0

是的,像這樣的人。儘管如此,我會使用映射而不是列表(即使大量線程正在等待也可以高效地檢索正確的鎖)。 – 2011-06-05 11:36:35

+0

作品像一個魅力,謝謝:) – kazu 2011-06-05 12:35:54

1

@ Paulo的解決方案是我之前使用過的。但是,可能有一個更簡單的解決方案。

假設您在連接中沒有後臺線程讀取結果。你可以做的是使用當前線程來讀取任何結果。

// Asynchronous call 
conn.sendMessage("Async-request"); 
// server sends no reply. 

// Synchronous call. 
conn.sendMessage("Sync-request"); 
String reply = conn.readMessage(); 
+0

是的,我之前使用過你的方法,但是這隻有在我知道接下來會出現什麼數據包時纔有效。如果存在後臺數據流,則下一個數據包可能是數據包,而不是我正在等待的ACK。這是我的問題。後臺線程主要處理數據包,因爲這很容易,不需要同步。 – kazu 2011-06-05 08:19:28

+0

如果您有任何背景數據流,我會使用單獨的連接。另一種選擇是讓readMessage等到它讀取一個匹配的requestId。您可以讓發送消息也檢查要讀取的數據。這僅在定期檢查流而不使用阻塞IO時纔有用。 – 2011-06-05 09:34:11

+0

@PeterLawrey既然你提到了Paulo的答案(並且似乎以類似的方式做),我還想知道你對我的評論的看法以及他的回答:) – cobby 2018-01-19 14:12:25