標籤:rmi rpc marshal unmarshal
用戶端代碼
public class RMIClient { public static void main(String... args) throws RemoteException, NotBoundException, MalformedURLException { if (args == null || args.length <= 1) { System.out.println("usage : java -jar RMIClient.jar rmi_server_ip content"); System.exit(0); } IRMIService service = (IRMIService) Naming.lookup("rmi://"+args[0]+":1099/RMIServer"); System.out.println(service.speakToYourself(args[1])); //speakToYourself 這個方法僅僅就是在輸入參數前面加上了另外一個字串而已,然後返回。 }}
抓包結果
上述代碼執行過程中,使用wireshark進行抓包,得到如下結果
抓包結果分析:
48-49 :tcp的三向交握
54-69 :通訊資料
70-72 :中斷連線
54-69中協議類型凡是tcp的,都是確認其他資料包的確認包,比如55號:
其他為rmi協議的才包含通訊資料。
矛盾
oracle文檔中介紹rmi的背景時說:
socket要求client和server參與到應用程式層協議,以便對交換的資訊進行編碼和解碼。這種協議的制定就是笨重的和容易出錯的。 官方串連:https://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-intro2.html
但是跟蹤rmi源碼可以知道,rmi同樣時通過socket來通訊的。所以這讓人感覺有點矛盾。。
源碼
Connection封裝
通過socket拿到Connection後,rmi對conn進行了tcpconnection的封裝
寫入rmi協議頭部資訊
對照抓取到的包資訊:
兩者是符合的。
原理分析
rmi底層採用了Stub 和 Skeletons機制。。
Skeletons:運行在server端,負責分發請求。接到client端的請求後,會做三件事:
- unmarshals 用戶端發送來的資料
- 根據收到的資料,執行相關的方法,拿到執行結果
- marshals執行結果,發送給client
Stub:運行在client端,需要調用遠程方法時,會做下面幾件事(基本和skeleton反著來)
- marshals待發送資料並發送
- 等待結果
- unmarshals收到的資料
個人認為那個skeletons就有點類似於spring的dispatcherservlet,當收到用戶端請求後,負責把請求分發到相應的controller進行處理。
針對上面給出的用戶端代碼,用戶端發送的資料就是 :
"rmi://"+args[0]+":1099/RMIServer"
接收到的結果就是:
IRMIService service //這樣一個執行個體
注意
拿到 IRMIService service 這樣一個執行個體後,當調用service的 service.speakToYourself(args[1])方法時,並沒有與伺服器通訊。
也就是說這個方法的執行是在本地執行,而非在遠程伺服器上執行後再回傳結果
注意,是 本地執行
那麼rmi說的遠程調用,怎麼體現遠程呢?
這個遠程調用指的是,用戶端發送”rmi://”+args[0]+”:1099/RMIServer”到擷取service執行個體的過程,這個是在遠程伺服器上執行的。
本質
跟蹤代碼執行過程可以知道,從用戶端發送資料,到拿到service執行個體的過程,其實就是
對象的序號–>網路傳輸–>還原序列化 的過程
上面提到的marshal 和 unmarshal的意思,其實就是 serialize和 unserialize的意思。這個可以查證wiki:https://en.wikipedia.org/wiki/Marshalling_(computer_science) ,在代碼上也的確是這麼回事。
補充閱讀:https://www.ietf.org/rfc/rfc2713.txt rmi協議rfc文檔
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
RMI執行過程分析