首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2EE

RMI 通信協議規範第十章全解:傳輸格式、HTTP 封裝與多路復用協議

     編輯:J2EE

協議概述:分層協議棧

RMI 協議建立在兩大基礎協議之上:

  • Java 對象序列化協議:編組調用參數與返回數據
  • HTTP 協議:作為「投遞」遠程調用的載體,穿透防火牆

語法規範採用類似 JLS 2.3 節的符號,控制碼用十六進制字面值表示。應用相關值的非終結符由其 Java 類型定義。

RMI 傳輸協議:流格式定義

通信格式以 Stream 表示,從客戶端視角:Out 為輸出消息,In 為輸入消息。輸入輸出流成對出現,In 僅需協議確認字節,其他標頭信息(魔數、版本)由流對上下文隱含。

輸出流格式

Out: Header Messages HttpMessage
Header: 0x4a 0x52 0x4d 0x49  // "JRMI"
Version Protocol
Version: 0x00 0x01
Protocol: StreamProtocol | SingleOpProtocol | MultiplexProtocol
StreamProtocol: 0x4b
SingleOpProtocol: 0x4c
MultiplexProtocol: 0x4d
Messages: Message Messages | Message
  • SingleOpProtocol:HTTP 嵌入調用,僅一個 Message,無額外封裝
  • StreamProtocol / MultiplexProtocol:服務器回應 0x4e(協議支持)+ EndpointIdentifier(主機名/端口),客戶端回應默認端點
  • StreamProtocol:端點協商後直接發送 Messages
  • MultiplexProtocol:套接字作為多路復用具體連接,虛擬連接承載 Messages

輸出消息類型

Message: Call | Ping | DgcAck
Call: 0x50 CallData
Ping: 0x52
DgcAck: 0x54 UniqueIdentifier
  • Call:編碼方法調用
  • Ping:傳輸層心跳,測試遠程 JVM 活性
  • DgcAck:確認已接收服務器返回的遠程對象(分布式 GC)

輸入流格式

In: ProtocolAck Returns ProtocolNotSupported HttpReturn
ProtocolAck: 0x4e
ProtocolNotSupported: 0x4f
Returns: Return Returns | Return
Return: ReturnData | PingAck
ReturnData: 0x51 ReturnValue
PingAck: 0x53

RMI 對象序列化協議應用

CallData 結構

CallData: ObjectIdentifier Operation Hash Arguments_opt
ObjectIdentifier: ObjectNumber | UniqueIdentifier
UniqueIdentifier: Number Time Count
Arguments: Value Arguments | Value
Value: Object | Primitive
  • ObjectIdentifier:調用目標
  • Operation:JDK 1.1 為方法號;JDK 1.2 為 -1
  • Hash:JDK 1.1 為 Stub/Skeleton 接口雜湊;JDK 1.2 為方法雜湊
  • Arguments:零個或多個參數(Object/Primitive)

ReturnValue 結構

ReturnValue:
  0x01 UniqueIdentifier Value_opt   // 正常返回
  0x02 UniqueIdentifier Exception   // 異常返回

注意:ObjectIdentifier、UniqueIdentifier、EndpointIdentifier 使用自定義 write 方法(非 writeObject),組件數據連續寫入流。

類註解與動態加載

  • ObjectOutputStream.annotateClass:查詢類加載器獲取 codebase URL,非空則寫入流
  • ObjectInputStream.resolveClass:讀取註解,非空則獲取 URL 類加載器加載類(同 Applet 類加載機制)
  • 建議不註解 java.* 包類(接收端必然可用)

RMI HTTP POST 封裝協議

用於防火牆穿透,URL 兩種形式:

  1. https://<host>:<port>/:直接與 RMI 服務器通訊
  2. https://<host>:80/cgi-bin/java-rmi?forward=<port>:調用 CGI 腳本轉發
HttpMessage: HttpPostHeader Header Message
HttpReturn: HttpResponseHeader Return
  • 僅 SingleOpProtocol 出現在 HttpMessage Header 中
  • HttpReturn 不含協議確認字節
  • 響應狀態碼非 200 視為無返回值
  • 單個 HTTP POST 請求僅嵌入一個 RMI 調用

應用相關值類型映射表

符號Java 類型
Countshort
Exceptionjava.lang.Exception
Hashlong
HostnameUTF
Numberint
Objectjava.lang.Object
ObjectNumberlong
Operationint
PortNumberint
Primitivebyte, int, short, long...
Timelong

RMI 多路復用協議:單連接承載多虛擬連接

目的:允許雙端點均可打開多個全雙工虛擬連接,解決 Applet 環境無法創建 ServerSocket 導出遠程對象的問題。

核心概念

  • 端點:多路復用連接的兩個用戶之一
  • 具體連接:底層雙向可靠字節流(通常為 TCP Socket)
  • 虛擬連接:代表特定會話的雙向可靠字節流
  • 多路復用連接:一個具體連接上所有虛擬連接的集合
  • 連接標識符:16 位整數標識虛擬連接(最多 65,536 個)

連接狀態機

狀態條件
打開發送/接收 OPEN 且未關閉
等待關閉發送 CLOSE 但未收到 CLOSE/CLOSEACK
關閉從未打開,或收到 CLOSE/CLOSEACK 且未重新打開

流控制:基於包的獨立緩衝

  • 每端點維護輸入請求數(已發送 REQUEST 未收到數據)與輸出請求數(對方請求未發送數據)
  • REQUEST 增加輸入請求數,TRANSMIT 減少輸出請求數
  • 規則:不得超過無阻塞處理能力;不得發送超過輸出請求數的數據
  • 實現必須能消耗所有準備好的協議數據,防止遞歸調用死鎖

協議記錄格式(可變長度)

第一字節操作碼:

值操作
0xE1OPEN
0xE2CLOSE
0xE3CLOSEACK
0xE4REQUEST
0xE5TRANSMIT

OPEN (0xE1)

1 byte opcode | 2 bytes ID
  • 高位分隔標識符空間:發起方用高位=1(0x8000-0xFFFF),另一方用高位=0(0x0000-0x7FFF)防衝突
  • 打開後雙端點輸入/輸出請求數歸零

CLOSE (0xE2)

1 byte opcode | 2 bytes ID
  • 發送端狀態變為「等待關閉」,不可重新打開直到收到 CLOSE/CLOSEACK
  • 接收端狀態變為「關閉」,需以 CLOSEACK 響應(若非已等待關閉)

CLOSEACK (0xE3)

1 byte opcode | 2 bytes ID
  • 確認收到 CLOSE,狀態從「等待關閉」轉為「關閉」,可重新打開

REQUEST (0xE4)

1 byte opcode | 2 bytes ID | 4 bytes count
  • 請求額外 count 字節數據(32 位有符號整數,必須 >0)
  • 發送端輸入請求數 +count;接收端輸出請求數 +count(若接收端非等待關閉)

TRANSMIT (0xE5)

1 byte opcode | 2 bytes ID | 4 bytes count | count bytes data
  • 實際傳輸數據,count >0 且不超過輸出請求數
  • 發送端輸出請求數 -count;接收端可讀隊列 +count 數據,輸入請求數 -count
  • 若輸入請求數歸零但用戶仍讀取,端點應發送新 REQUEST

協議違規處理

任何違規或通訊錯誤導致多路復用連接立即關閉:實際連接終止,所有虛擬連接即時關閉,用戶仍可讀取已緩衝數據。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved