guacamole协议及命令详解
guacamole協議
協議組成
Guacamole 協議由若干指令組成。每條指令是一個逗號分隔的列表,最后以分號終止,其中列表中的第一個元素是指令操作碼,其后的元素是該指令的參數:
OPCODE,ARG1,ARG2,ARG3,……;指令列表中的每個元素都是由一個正的十進制整數前綴和一個具體的元素值組成,其中前綴由一個英文句點( . )分隔。整數前綴表示具體的元素值的Unicode字符的數量,字符由UTF-8編碼:
LENGTH.VALUE若干條完整的指令組成一條消息,該消息從客戶端發送到服務器,或者從服務器發送到客戶端。客戶端到服務器的指令通常是控制指令(用于建立連接或斷開連接)和事件(鼠標事件和鍵盤事件)。服務器到客戶端的指令通常是將客戶端用作遠程顯示器的繪制指令(緩存,剪切,繪制圖像)。
例如,將顯示尺寸設置為1024*768的完整有效的指令是:
4.size,1.0,4.1024,3.768;對于這個指令,會被服務器解析為四個元素:“size” ,作為size指令的操作碼,“0”,是圖像默認層的索引,“1024”,為所需的像素寬度,“768”,為所需的像素高度。
正因為Guacamole協議的設計方式,使得它可以流式傳輸協議,同時也可以很容易地被JavaScript解析。
JS確實原生支持類似于XML、JSON這樣格式的信息,但是這類格式的信息都不能以流的形式傳輸。JS在解析這類格式的信息前必須接收到完整的XML或者JSON的包,而guacamole協議的信息,卻可以一邊接收一邊解析。它的指令內每個元素的長度前綴使得解析器不用遍歷每個字符就可以完成指令之間的跳轉。
握手階段
新建連接
握手的過程是guacamole協議建立連接的過程。當客戶端發送“select”指令時,握手階段就開始了,select指令告訴服務器要加載哪個協議:
6.select,3.vnc;收到“select”指令后,服務器會加載對應的客戶端組件,并且回復一個“args”指令,這個指令指明了服務器端需要的參數列表
4.args,13.VERSION_1_1_0,8.hostname,4.port,8.password,13.swap-red-blue,9.read-only;其中的協議版本用于協商客戶端和服務器的不同版本之間的兼容性,從而允許雙方協商最高支持的版本并啟用或禁用與該版本關聯的功能。不支持該指令的舊版本的Guacamole客戶端將無提示地將其忽略為空連接參數。有效協議版本如下:
| 1.0.0 | VERSION_1_0_0 | 這是默認版本,適用于1.1.0之前的任何版本。協議的版本1.0.0不支持協議協商,并且要求握手指令以特定順序傳遞,并且存在(即使為空)。 |
| 1.1.0 | VERSION_1_1_0 | 協議版本1.1.0引入了對協議版本協商,握手指令的任意順序的支持,并支持在握手期間傳遞時區指令。 |
客戶端接收到服務端可接受的參數列表后,需要回復給服務器,自己支持的音頻(audio),視屏(video),圖像(image),最佳屏幕尺寸(size)及時區(timezone),并在最后回復所有的服務器要求的參數的值(connect),即使是空,也要回復。任意要求沒有滿足,連接都將被關閉??蛻舳嘶貜徒o服務端的消息如下:
4.size,4.1024,3.768,2.96; 5.audio,9.audio/ogg; 5.video; 5.image,9.image/png,10.image/jpeg; 8.timezone,16.America/New_York; 7.connect,13.VERSION_1_1_0,9.localhost,4.5900,0.,0.,0.;如上,在客戶端回復服務端的參數中,回復了三個0.給服務端,意味著客戶端這三個參數為空,沒有值,所以留空,長度為0,回復0.恰好。
在實際協議中,指令之間是緊挨著的不存在換行符,如果一條指令之后除了新指令的開始之外還有其他內容,則連接將關閉。
以下是握手過程中的指令的說明:
| audio | 客戶端支持的音頻編碼解碼器。在上面的例子中指定了audio/ogg作為支持的編碼解碼器 |
| connect | 這是握手階段的最后一個指令,它表明握手階段已經結束,并且連接正常建立,可以繼續進行。這條指令后續跟著服務器中args指令發送的連接參數的參數值。在上面的例子中,參數指定了在5900端口與localhost進行連接,后續三個參數值為空。 |
| iamge | 客戶端支持的圖像格式,按首選項順序。上例中的客戶端同時支持PNG和JPEG。 |
| timezone | 客戶端的時區,采用IANA區域密鑰格式。上例的時區是美國紐約 |
| video | 客戶端支持的視頻編碼解碼器。上例的客戶端是不支持任何視頻編解碼器。 |
客戶端在握手中發送的指令的順序是任意的,除了最后一條指令connect將結束握手并嘗試開始連接。
客戶端發送完這些指令后,服務器將嘗試使用接收到的參數初始化連接,如果成功,則以“ready”指令進行響應。這條指令中包含新客戶端連接的ID,并標記交互階段的開始。這個ID是一個任意字符串,但是保證這個ID在所有活動鏈接以及受支持協議中是唯一的:
5.ready,37.$260d01da-779b-4ee9-afc1-c16bae885cc7;當服務器發送ready后,真正的交互階段就開始了??蛻舳撕头掌鞫酥g相互傳遞繪圖和事件指令,直到關閉連接。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OmgcnZVV-1610937685314)(/Users/wengcy/Library/Application Support/typora-user-images/image-20201230202507183.png)]
加入現有連接
握手階段完成后,如果通過“select”指令提供了ID而不是協議名稱,則這個連接將被認為是活躍的并且是能被加入的。
6.select,37.$260d01da-779b-4ee9-afc1-c16bae885cc7;加入現有連接的其余階段與握手的階段是相同的。與新連接一樣,這次連接的其他參數由握手期間提供的參數值決定。
流與對象
guacamole支持傳輸剪切板內容,音頻內容,視頻內容和圖像數據,以及文件和任意的命名管道。
特殊語義的指令將會通過新分配的流傳送。例如,用于播放媒體文件的“audio”或“video”指令。用于傳輸文件的“file”指令,用于在客戶端和服務端傳輸任意數據的“pipe”指令。在某些情況下,將通過已命名的流傳送的結構化集合對象的方式來顯示指明流的能力范圍和語義。
流一旦被創建,將通過“blob”指令一塊一塊地傳送數據,通過“ack”來確認已收到的消息,流的結束通過一個“end”指令來標識。
常用協議指令解析
命令解析參考官方文檔與客戶端源碼,由一次真實的鏈接作為樣例參考
樣例
在一次訪問中遇到的,除了建立連接的指令外的指令
read請求:
4.size,1.0,4.1364,3.768;4.size,2.-1,2.11,2.16;3.img,1.3,2.12,2.-1,9.image/png,1.0,1.0;4.blob,1.3,232.iVBORw0KGgoAAAANSUhEUgAAAAsAAAAQCAYAAADAvYV+AAAABmJLR0QA/wD/AP+gvaeTAAAAYklEQVQokY2RQQ4AIQgDW+L/v9y9qCEsIJ4QZggoJAnDYwAwFQwASI4EO8FEMH95CRYTnfCDOyGFK6GEM6GFo7AqKI4sSSsCJH1X+roFkKdjueABX/On77lz2uGtr6pj9okfTeJQAYVaxnMAAAAASUVORK5CYII=;3.end,1.3;6.cursor,1.0,1.0,2.-1,1.0,1.0,2.11,2.16;4.size,2.-1,2.32,2.32;0.;
3.img,1.3,2.14,4.-885,9.image/png,1.0,1.0;4.blob,1.3,152.iVBORw0KGgoAAAANSUhEUgAAAI8AAACfAQMAAAD6exhLAAAABlBMVEUAAAA4bKCQgYqkAAAAJUlEQVRIie3IMQ0AAAgDsDnBv0ngxwFJezY1R0cppZRSSin1oBaBVZeq9v/dCwAAAABJRU5ErkJggg==;3.end,1.3;4.copy,4.-885,1.3,1.0,3.140,3.159,2.14,1.0,3.971,3.257;4.sync,11.14688328152;
4.rect,1.0,3.994,3.263,2.42,2.12;5.cfill,2.14,1.0,1.8,2.36,3.104,3.255;
5.error,18.Aborted. See logs.,3.520;7.dispose,3.-46;0.;
7.dispose,3.-28;7.dispose,3.-45;7.dispose,3.-37;7.dispose,2.-1;7.dispose,1.0;10.disconnect;
write請求:
3.ack,1.3,2.OK,1.0;
4.sync,11.14685868962;
3.nop;
5.mouse,3.702,2.16,1.0;
3.key,3.115,1.1;
說明:read請求中出現的命令為guacd向客戶端發送的命令,write請求中出現的命令為客戶端向guacd發送的命令。read請求,write請求均為客戶端發出,指令分別出現在write請求的請求頭的requestPayload部分和read請求的response部分。
指令解析
size
設置指定圖層的大小,擁有三個參數
layer:指定圖層下標
width:指定圖層寬度
height:指定圖層高度
完整指令順序:
size,layer,width,height;
樣例:
4.size,1.0,4.1364,3.768;
作用:將圖層0的大小設置為1364*768
img
分配一個新的流,并將其與圖像更新的元數據相關聯,包括圖像類型,目標圖層和目標坐標。圖像的具體內容將由其后的Blob指令指定,因此img通常與Blob綁定使用。
img指令有六個參數
stream:要分配的流的索引
mask:繪制圖像時要應用的通道蒙版(具體見最后通道蒙版表)
layer:圖像生成的目標圖層
mimetype:發送的圖像的格式
x:目標圖像左上角在目標圖層中的X坐標
y:目標圖像左上角在目標圖層中的Y坐標
完整指令順序:
img,stream,mask,layer,mimetype,x,y;
樣例:
3.img,1.3,2.12,2.-1,9.image/png,1.0,1.0;
作用:通知客戶端將要使用索引為3的流傳輸圖像,繪圖時使用12號通道蒙版,圖像生成在-1號圖層(負數圖層在客戶端中作為緩沖圖冊,存儲各種緩沖圖像),圖像格式為
image/png,坐標為(0,0)
blob
通過給定的流發送一組數據。數據長度任意,采用base64編碼,該數據僅當通過指定流傳輸時才有意義。
blob指令有兩個參數
stream:指定流的索引
data:要發送的base64編碼數據
完整指令順序:
blob,stream,data;
樣例:
4.blob,1.3,232.iVBORw0KGgoAA…………(具體數據省略)
作用:通過索引為3的流傳輸數據
end
結束指定流,體現在客戶端源碼中為將指定索引流刪除。
end指令有一個參數
stream:指定流的索引
完整指令順序:
end,stream;
樣例:
3.end,1.3;
作用:將索引為3的流關閉
cursor
將客戶端的光標設置為具有指定熱點的圖層的指定矩形中的圖像數據。有七個參數
X:光標熱點的X坐標
Y:光標熱點的Y坐標
srclayer:要復制圖像的圖層的索引
srcX:源圖層內源矩形左上角的X坐標
srcY:源圖層內源矩形左上角的Y坐標
srcWidth:源圖層內源矩形的寬度
srcHeight:源圖層內源矩形的高度
完整指令順序:
cursor,X,Y,srcLayer,srcX,srcY,srcWidth,srcHeight;
樣例:
6.cursor,1.0,1.0,2.-1,1.0,1.0,2.11,2.16;
作用:從-1圖層(0,0)位置拷貝一個11*16的矩形作為光標圖像生成在(0,0)位置
copy
將圖像數據從指定圖層或緩沖區的指定矩形復制到另一個指定圖層或緩沖區的其他位置。
copy指令有九個參數:
srclayer:源圖層的索引
srcx:源圖層內源矩形左上角的X坐標
srcy:源圖層內源矩形左上角的Y坐標
srcwidth:源圖層內源矩形的寬度
srcheight:源圖層內源矩形的高度
mask:在目標圖層上繪制圖像數據時要應用的通道蒙版
dstlayer:要繪制圖像的目標圖層的索引
dstx:目標圖層內目標圖像的左上角的X坐標
dsty:目標圖層內目標圖像的左上角的Y坐標
完整指令順序:
copy,srclayer,srcx,srcy,srcwidth,srcheight,mask,dstlayer,dstx,dsty;
樣例:
4.copy,4.-885,1.3,1.0,3.140,3.159,2.14,1.0,3.971,3.257;
作用:
從索引為-885的圖層中復制(3,0)位置的140*159大小的矩形到索引為0的圖層的(971,257)位置,繪圖時采用14號通道蒙版
sync
服務器指示給定的時間戳是所有先前操作的當前時間戳??蛻舳吮仨氻憫盏降拿總€sync指令。
客戶端和服務器均應偶爾發送sync指令以報告當前操作執行狀態。如果客戶端沒有響應服務器的sync指令,服務器可能會停止發送更新,直到客戶端趕上來。
sync指令只有一個參數:
timestamp:有效的服務器相對時間戳
完整指令順序:
sync,timestamp;
樣例:
4.sync,11.14688328152;
作用:指示當前時間戳為14688328152
rect
將一個矩形路徑添加到指定的圖層
rect指令文檔上有6個參數
mask:繪制圖像數據時要應用的通道蒙版
layer:目標層
X:要繪制的矩形的左上角的X坐標
Y:要繪制的矩形的左上角的Y坐標
width:要繪制的矩形的寬度
height:要繪制的矩形的高度
客戶端源碼與網頁請求數據中只有5個參數:
layer:目標層
X:要繪制的矩形的左上角的X坐標
Y:要繪制的矩形的左上角的Y坐標
width:要繪制的矩形的寬度
height:要繪制的矩形的高度
按照客戶端源碼與網頁請求數據為準
指令完整順序:
rect,layer,x,y,width,height;
樣例:
4.rect,1.0,3.994,3.263,2.42,2.12;
作用:
在0號圖層(994,263)位置繪制一個42*12的矩形
cfill
用指定的顏色填充當前路徑。
cfill指令有六個參數
mask:在指定層中填充當前路徑時要應用的通道蒙版
layer:要填充路徑所在的圖層
r:用于填充路徑的顏色的紅色分量
g:用于填充路徑的顏色的綠色分量
b:用于填充路徑的顏色的藍色分量
a:用于填充路徑的顏色的alpha分量(不透明度)
完整指令順序:
cfill,mask,layer,r,g,b,a;
樣例:
5.cfill,2.14,1.0,1.8,2.36,3.104,3.255;
作用:
使用14號蒙版填充0號圖層中當前存在的路徑,rgb顏色分量為(8,36,104),不透明度為255
error
通知客戶端由于指定的錯誤即將關閉連接。服務器可以在任何階段發送此消息。
error指令有兩個參數
test:描述錯誤的消息
status:描述錯誤的guaca協議代碼(具體見最后狀態碼表)
完整指令順序:
error,test,status;
樣例:
5.error,18.Aborted. See logs.,3.520;
作用:通知客戶端發送520異常,具體原因查詢日志。
dispose
刪除指定的圖層
dispose指令只有一個參數
layer:要刪除的圖層
完整指令順序:
dispose, layer;
樣例:
7.dispose,2.-1;
作用:
刪除-1圖層
disconnect
通知客戶端服務器即將關閉連接。該指令不帶任何參數。
ack
ack指令確認接收到的數據blob,并提供狀態代碼和消息,說明該blob關聯的操作是成功還是失敗。
ack指令有三個參數:
stream:接收相應blob的流的索引
message:提示消息
status:表示成功或失敗的狀態碼
完整指令順序:
ack,stream,message,status;
樣例:
3.ack,1.3,2.OK,1.0;
作用:索引為3的流中的blob接收成功
nop
客戶端“ nop”指令不執行任何操作,沒有任何參數,并且被Guacamole服務器忽略。它的主要用途是作為保持活動信號,并且在沒有活動可確保套接字由于超時而沒有關閉的情況下,可由Guacamole 客戶端發送。
mouse(客戶端指令)
發送指定的鼠標移動或按鈕按下或釋放事件
mouse指令有三個參數
X:鼠標指針的當前X坐標。
Y:鼠標指針的當前Y坐標。
mask:按鈕掩碼,代表每個鼠標按鈕的按下或釋放狀態
按鈕掩碼:
0-未按下鼠標
1-按下鼠標左鍵
2-按下鼠標中鍵
4-按下鼠標右鍵
8-鼠標滾輪向上
16-鼠標滾輪向下
完整指令順序:
mouse,x,y,mask;
樣例:
5.mouse,3.702,2.16,1.0;
作用:
鼠標移動到坐標(702,16)位置,未按下任何鍵
key(客戶端指令)
發送指定的按鍵按下或釋放事件
keysym:按下或釋放的鍵對應的X11值
pressed:如果未按下該鍵,則為0;如果按下該鍵,則為1。
完整指令順序:
key,keysym,pressed;
樣例:
3.key,3.115,1.1;
作用:
按下115對應的鍵(S鍵)
圖像繪制
圖像數據
guacamole協議通過img指令建立一個流,用于以PNG,JPEG或者WebP的格式傳送的圖像數據。根據使用的格式的不同,通過這種方式傳送的圖像更新數據可能以RGB或者RGBA(A代表透明度)編碼,如果通過libguac傳送的話,還會被自動調色。
圖像數據還可以被發送到任意指定的矩形、圖層或者緩存。將圖像數據發送到圖層意味著立刻可見,將圖像數據發送到緩存意味著可以在將來被重用。
圖像數據可以在圖層或者緩存之間拷貝,這在屏幕滾動的時候很有用(屏幕滾動的時候,更新的圖像經常與之前的圖像完全一樣),在緩存某個部分的圖像的時候,也很有用。
guacamole吸收了RDP和VNC中拷貝屏幕區域數據的概念,并將之進一步發展,將屏幕可見(圖層)與屏幕不可見(緩存)的存儲統一起來。使用copy指令可以拷貝一個矩形的圖像數據,并且可以將其放置到其他任意圖層包括緩存中。
緩存與圖層
guacamole里的每個繪制操作都會作用到一個具體的圖層,每個圖層都有一個唯一的編號來標識它自身。圖層編號是負數的時候,這個圖層是不可見的,可以用于存儲或者緩存圖像數據。此時,圖層通過編碼被引用,且等同于文檔中所謂的緩存,當通過某個指令引用圖層的時候,圖層會被自動創建。
0號圖層會被當作默認圖層,調整這個圖層大小的時候就會調整整個遠端屏幕大小。其他圖層創建時候的初始大小與默認圖層的大小一致。緩存的初始大小為0x0,并且會自動調整大小來適配裝入的內容。
非緩存圖層可以在其他圖層中被移動或者嵌套。通過這種方式,提供了一種簡單的硬件加速合成圖像的方式。如果你需要一個窗口浮現在另外的窗口之上,或者你想要移除一些對象,又或者你想要自動保存一些對象之下的圖像數據,圖層是實現這些需求較好的方式。如果一個圖層嵌套在其他圖層里,它的位置是相對于父圖層的。當父圖層被移動或者重排序(調整圖層之間的順序)的時候,子圖層會隨之移動以及重排序。如果子圖層超出了父圖層的邊界,字圖層將被裁切。
繪制過程
當客戶端與guacd成功建立連接后,客戶端將開始進行圖像繪制,圖像刷新主要以img指令配合blob指令為主。guacamole協議通過img指令建立一個流,隨后blob指令沿流開始傳輸數據(base64格式),客戶端收到數據解析后就將按照img指令指定的繪制方式(通道蒙版樣式)將圖形繪制在指定的圖層上。在視頻播放(存疑,實際與官方文檔有出入)時,同理guacd不斷向客戶端沿流傳輸img指令+blob指令的組合,客戶端收到指令后進行解析繪制,只要流中一直傳輸img指令+blob指令,視頻就將播放下去。guaca采用局部刷新的方式,
copy與cfill指令也參與圖像刷新,copy指令通常是從緩沖圖層獲取數據,常見于屏幕滾動, 光標變化時刷新;cfill指令則配合rect指令負責填充指定矩形。
附錄
狀態碼表
| 0 | SUCCESS | 操作成功。OK |
| 256 | UNSUPPORTED | 請求的操作不受支持 |
| 512 | SERVER_ERROR | 發生內部錯誤,無法執行操作 |
| 513 | SERVER_BUSY | 由于服務器繁忙,無法執行該操作 |
| 514 | UPSTREAM_TIMEOUT | 上游服務器沒有響應。在大多數情況下,上游服務器是遠程桌面服務器。 |
| 515 | UPSTREAM_ERROR | 上游服務器遇到錯誤。 |
| 516 | RESOURCE_NOT_FOUND | 找不到關聯的資源,例如文件或流,因此操作失敗。 |
| 517 | RESOURCE_CONFLICT | 資源已被使用或鎖定,從而阻止了所請求的操作。 |
| 518 | RESOURCE_CLOSED | 請求的操作無法繼續,因為相關的資源已關閉。 |
| 519 | UPSTREAM_NOT_FOUND | 上游服務器似乎不存在,或者無法通過網絡訪問。 |
| 520 | UPSTREAM_UNAVAILABLE | 上游服務器拒絕服務連接。 |
| 521 | SESSION_CONFLICT | 上游服務器內的會話已結束,因為它與另一個會話發生沖突。 |
| 522 | SESSION_TIMEOUT | 上游服務器內的會話已結束,因為它似乎處于非活動狀態。 |
| 523 | SESSION_CLOSED | 上游服務器中的會話已被強制關閉。 |
| 768 | CLIENT_BAD_REQUEST | 請求的參數無效或無效。 |
| 769 | CLIENT_UNAUTHORIZED | 權限被拒絕,因為該用戶尚未登錄。 |
| 771 | CLIENT_FORBIDDEN | 權限被拒絕,登錄將無法解決問題。 |
| 776 | CLIENT_TIMEOUT | 客戶端花費的時間太長而無法反映 |
| 781 | CLIENT_OVERRUN | 客戶端發送的數據超出了協議允許的范圍。 |
| 783 | CLIENT_BAD_TYPE | 客戶端發送了意外或非法類型的數據。 |
| 797 | CLIENT_TOO_MANY | 客戶端已經使用了太多資源。在允許進一步的請求之前,必須釋放現有資源。 |
通道蒙版表
| 0x00 | CLear | 在目標圖層中清空所有已經存在的圖像數據。 |
| 0x01 | B in A | 在源圖層不透明的地方繪制目標圖層,清空所有的源圖層透明以及目標圖層透明的地方。 |
| 0x02 | B out A | 清除目標圖層中源圖層不透明的部分,而且不會繪制任何東西??捎糜谡谏w。 |
| 0x03 | B | 不做任何操作 |
| 0x04 | A in B | 在目標圖層不透明的地方繪制源圖層,清空所有的源圖層透明以及目標圖層透明的地方。 |
| 0x05 | A xnor B | 將目標圖層與源圖層不透明的地方相加起來。清空所有目標圖層與源圖層透明的地方。這與A+B相似,除了透明的地方要清空以外。 |
| 0x06 | A atop B | 在目標圖層中填充源圖層中不透明的部分。 |
| 0x07 | (A+B)atop B | 在目標圖層不透明的地方繪制源圖層,并保留目標圖層余下的地方。 |
| 0x08 | A out B | 在目標圖層透明的地方畫源圖層,清空所有的源圖層不透明以及目標圖層不透明的地方。 |
| 0x09 | B atop A | 在源圖層不透明的地方填充目標圖層。 |
| 0x0A | A xor B | 與邏輯上的xor操作一樣。但是這是圖像合成操作,不是位運算,實質含義是在目標圖層的透明部分繪制源圖層,在源圖層的透明部分繪制目標圖層。 |
| 0x0B | B over A | 與你期望的常規繪制相反。源圖層出現在目標圖層透明的地方。如同你將目標圖層繪制在源圖層上,而不是反過來。 |
| 0x0C | A | 填充源圖層,忽略目標圖層。 |
| 0x0D | (A+B)atop A | 在源圖層不透明的地方繪制目標圖層,并且復制源圖層余下的地方。 |
| 0x0E | A over B | 最常見的合成操作,在目標圖層繪制全部的源圖層,除了源圖層透明的地方。 |
| 0x0F | A + B | 將源圖層與目標圖層相加起來,并將結果填充到空白的畫布上。 |
總結
以上是生活随笔為你收集整理的guacamole协议及命令详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新视野第二册课后翻译答案
- 下一篇: 音乐播放曲谱图(柱形和曲线图谱)