文档章节

Spice Agent Protocol (对官网文档的翻译及一些个人理解)

Amui
 Amui
发布于 2017/03/28 22:51
字数 2918
阅读 789
收藏 1

vdagent virtio serial port

在客户机(guest)端的所有虚拟通信都将通过单个管道运行,该管道作为virtio串行端口呈现给客户机系统。 在windows下,这个virtio串口有以下名称:

\\\\.\\Global\\com.redhat.spice.0

在Linux下,这个virtio串口有以下名称:

/dev/virtio-ports/com.redhat.spice.0

当设置如下参数时,Qemu将启用virtio串行端口。

-device virtio-serial-pci,id=virtio-serial0,max_ports=16,bus=pci.0,addr=0x5 -chardev spicevmc,name=vdagent,id=vdagent -device virtserialport,nr=1,bus=virtio-serial0.0,chardev=vdagent,name=com.redhat.spice.0

vdagent data chunks / ports

vdagent 通过virtio串行端口与spice 服务端连接,可以发送数据到spice服务器和spice客户端,也可以接收它们发过来的数据。为了使这成为可能,所有在vdagent virtio串行端口中的数据都要以VDIChunkHeader作为前缀,既消息头。

typedef struct SPICE_ATTR_PACKED VDIChunkHeader {
  uint32_t port;
  uint32_t size;
} VDIChunkHeader;

其中,size是发送或接收到的数据的大小,包括可变数据部分的大小,换句话说,size=sizeof(VDAgentMessage)+variable_data_len。(variable_data_len指的是可变长度数据的大小)

元素port的值则是以下其中之一:

enum {
    VDP_CLIENT_PORT = 1,
    VDP_SERVER_PORT,
};

当vdagent接收消息,元素port的值指示消息来自哪里。当vdagent发送消息,port表示消息的接收方。当要发送的消息是对已接收消息的应答消息,port的值应与已接收到的消息中的port值一致,要发送的消息是对其的回复。

当spice服务器接收到一条消息时,服务器会去掉消息中的chunk头(即VDIChunkHeader),然后根据在chunk头中的port数据,来决定是将数据直接发给客户端还是自己进行处理,或者,如果port值是一个无效值,则会记录错误并且丢弃该消息。

注意,目前还没有来自Agent的消息是针对服务器的,因此所有agent发送的port值为VDP_SERVER_PORT的消息都会被服务器默默的删除掉。

vdagent message struct

Agent发送或接收到的消息都被封装到一个叫VDAgentMessage的结构体中:

typedef struct SPICE_ATTR_PACKED VDAgentMessage {
  uint32_t protocol;
  uint32_t type;
  uint64_t opaque;
  uint32_t size;
  uint8_t data[0];
} VDAgentMessage;

其中,protocol元素的值始终都是VD_AGENT_PROTOCOL。

type元素的值来自以下的枚举值:

enum {
    VD_AGENT_MOUSE_STATE = 1,
    VD_AGENT_MONITORS_CONFIG,
    VD_AGENT_REPLY,
    VD_AGENT_CLIPBOARD,
    VD_AGENT_DISPLAY_CONFIG,
    VD_AGENT_ANNOUNCE_CAPABILITIES,
    VD_AGENT_CLIPBOARD_GRAB,
    VD_AGENT_CLIPBOARD_REQUEST,
    VD_AGENT_CLIPBOARD_RELEASE,
    VD_AGENT_FILE_XFER_START,
    VD_AGENT_FILE_XFER_STATUS,
    VD_AGENT_FILE_XFER_DATA,
    VD_AGENT_CLIENT_DISCONNECTED,
    VD_AGENT_MAX_CLIPBOARD,
    VD_AGENT_END_MESSAGE,
};

元素opaque是消息类型的占位符,只需要将单个整数作为消息数据传递,对于具有更多数据的消息类型,opaque值始终设置为0.

元素size指的是可变长度数据的大小。注意,在virtio端口中的完整数据的大小等于sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + variable_data_len. (variable_data_len指的是可变长度数据的大小,即VDAgentMessage中的size值)。

data是可变长度数据的起始地址。可变长度数据的内容取决于数据类型,对于大多数消息,它是一种数据类型特定的结构体,如VDAgentMouseSate。注意,元素data声明为0大小的数组,意味着该元素不会占用该结构体的任何内存,而且能轻松确定数据的起始地址。

 

vdagent messages

VD_AGENT_MOUSE_STATE

spice支持两种鼠标模式,server和client。

在server模式下,QEMU ps/2鼠标仿真用于向客户机(Guest)发送鼠标状态。当用户点击进spice客户端窗口时,客户端鼠标被捕获,且客户端将鼠标移动动作作为增量坐标发送到客户机(Guest)。

在client模式下,鼠标坐标位置作为绝对值发送给客户机。这需要用到usb表仿真,或者需要将它们发送到vdagent,再由vdagent通知客户机系统鼠标的位置(以及按钮点击)。

当鼠标处于client模式时,spice服务器会发送包含了鼠标状态更新的VD_AGENT_MOUSE_STATE消息。这些消息的变量数据由以下结构组成:

typedef struct SPICE_ATTR_PACKED VDAgentMouseState {
  uint32_t x;
  uint32_t y;
  uint32_t buttons;
  uint8_t display_id;
} VDAgentMouseState;

请注意,这些消息是由spice 服务器发送的,不是由spice 客户端发送的,因为服务器执行所有的鼠标处理(如在vdagent连接/断开连接时切换client模式或server模式)。

VD_AGENT_MONITORS_CONFIG

当客户端以全屏自动配置模式运行时,这个类型的消息将会被发送到Agent。该消息包含连接到客户端机器的显示器的信息。当收到该类型消息时,agent会尽可能的重新配置客户机中的qxl vga设备的输出,来匹配消息中的输出。例如,如果客户端具有的输出比在虚拟机中配置的更多,那么已有的输出应该被重新配置以匹配消息中的内容。

这些消息的变量数据由以下结构组成:

typedef struct SPICE_ATTR_PACKED VDAgentMonitorsConfig {
  uint32_t num_of_monitors;
  uint32_t flags;
  VDAgentMonConfig monitors[0];
} VDAgentMonitorsConfig

紧跟着该消息后面的是num_of_moniters个以下结构体:

typedef struct SPICE_ATTR_PACKED VDAgentMonConfig {
  uint32_t height;
  uint32_t width;
  uint32_t depth;
  int32_t x;
  int32_t y;
} VDAgentMonConfig;

当agent完成了输出的配置,agent应该返回VD_AGENT_REPLY消息,该消息中的type值设置为VD_AGENT_MONITORS_CONFIG,error值设置为VD_AGENT_SUCCESS或者VD_AGENT_ERROR,以指示配置输出是成功或是错误。

VD_AGENT_REPLY

该消息由vdagent发送给client,表示vdagent已经完成了对VD_AGENT_MONITORS_CONFIG 或者 VD_AGENT_DISPLAY_CONFIG消息的处理,以及处理成功还是失败。

typedef struct SPICE_ATTR_PACKED VDAgentReply {
  uint32_t type;
  uint32_t error;
} VDAgentReply;

enum {
  VD_AGENT_SUCCESS = 1,
  VD_AGENT_ERROR,
};

 

Clipboard(剪贴板)

spice允许spice客户端(client)与客户机(guest)之间通过客户机中的agent来共享剪贴板数据。

为此,客户机上的agent和客户端起着对称的作用:它们都可以声明拥有权(GRAB),释放拥有权(RELEASE),请求剪贴板数据(REQUEST)以及发送剪贴板数据。例如,当在某个应用中进行了copy的操作之后收到了剪贴板数据可用的系统通知,GRAB消息就会发送给对方(当任意一方收到了GRAB消息,就可以请求数据,若事先没有收到对方发来的GRAB消息的话,不会主动request数据)。当剪贴板清空时,grab信息必须被释放(release)掉。

另一端在grab处于活动状态时可以请求数据,之后会收到对方回复的带有剪贴板信息的CLIPBOARD消息(即对方发来REQUEST请求的话,就要返回CLIPBOARD类型数据)。

剪贴板数据类型如下:

enum {
  VD_AGENT_CLIPBOARD_NONE = 0,
  VD_AGENT_CLIPBOARD_UTF8_TEXT,
  VD_AGENT_CLIPBOARD_IMAGE_PNG,  /* All clients with image support should support this one */
  VD_AGENT_CLIPBOARD_IMAGE_BMP,  /* optional */
  VD_AGENT_CLIPBOARD_IMAGE_TIFF, /* optional */
  VD_AGENT_CLIPBOARD_IMAGE_JPG,  /* optional */
};

如果双方都实现了VD_AGENT_CAP_CLIPBOARD_SELECTION的功能,clipboard信息前面都要带有一个uint8_t的值,指示要操作的clipboard selection。关于clipboard selection,该协议末尾有关于VD_AGENT_CAP_CLIPBOARD_SELECTION的文档解释。

VD_AGENT_CLIPBOARD

struct VDAgentClipboard {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t type;
  uint8_t data[0];
};

VD_AGENT_CLIPBOARD 用于发送剪贴板数据。除非接收到VD_AGENT_CLIPBOARD_REQUEST 的数据请求,否则这个数据不会被发送,以避免浪费宽带。被传送的剪贴板数据通常来说都挺大的,在这种情况下,可以预期到,要传送的数据是要分割成多个VD_AGENT_MESSAGE发送的。

其中,type值是剪贴板数据类型,即前面剪贴板数据枚举类型中的一个。

data是可变长度数据的起始地址。在VD_AGENT_CLIPBOARD类型的消息中,data指向的应该是存放具体的剪贴板数据内容的内存的首地址。注意,元素data声明为0大小的数组,意味着该元素不会占用该结构体的任何内存,而且能轻松确定数据的起始地址。

VD_AGENT_CLIPBOARD_REQUEST

struct VDAgentClipboardRequest {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t type;
};

请求具有指定类型的剪贴板数据。

type值是剪贴板数据类型,即前面剪贴板数据枚举类型中的一个。

VD_AGENT_CLIPBOARD_GRAB

struct VDAgentClipboardGrab {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t types[0];
};

抓取剪贴板。个人理解为声明剪贴板拥有权。发出grab消息的A方声明了B方的剪贴板的拥有权,当B方要访问B方自己的剪贴板时,将会向发出grab消息的A方发送VD_AGENT_CLIPBOARD_REQUEST类型数据,A方接收到REQUEST信息后,就会发送CLIPBOARD信息给B方,且CLIPBOARD中的数据类型就是该GRAB中的types中的类型。

VD_AGENT_CLIPBOARD_RELEASE

struct VDAgentClipboardRelease {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
};

释放剪贴板。(例如当剪贴板变空时)

重要:

如果一条GRAB信息已被发送并且在当前处于活动状态,然后又从对方那里接收到连续的GRAB信息,这时不应该发送RELEASE信息给对方来取消上一条发过去的grab。因为那条处于活动状态的grab已经被对方暗中释放掉了。如果给对方发送额外的RELEASE信息,只会使对方感到困惑。

VD_AGENT_DISPLAY_CONFIG

该类型数据被spice客户端发送给vdagent,来通知vdagent一些特殊性能的相关设置。客户端可以要求vdagent禁用客户机系统的的许多功能,如字体反混叠等,以提高性能。vdagent可以在这些方面做一些努力,尤其是因为大多数设置都是以windows为中心的。应该使用VD_AGENT_REPLY 返回成功状态,除非出现问题。(这个类型数据没仔细看过)

typedef struct SPICE_ATTR_PACKED VDAgentDisplayConfig {
  uint32_t flags;
  uint32_t depth;
} VDAgentDisplayConfig;

enum {
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_WALLPAPER = (1 << 0),
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_FONT_SMOOTH = (1 << 1),
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_ANIMATION = (1 << 2),
  VD_AGENT_DISPLAY_CONFIG_FLAG_SET_COLOR_DEPTH = (1 << 3),
};

VD_AGENT_ANNOUNCE_CAPABILITIES

该数据类型可以(并且应该)被spice客户端和vdagent发送。它宣布了发送方将会发送哪些信息/能力(一个capability可以包含多条信息)或者当收到这些数据时知道怎么处理。此消息的目的是允许不同版本的客户端和vdagent能一起工作。

typedef struct SPICE_ATTR_PACKED VDAgentAnnounceCapabilities {
  uint32_t  request;
  uint32_t caps[0];
} VDAgentAnnounceCapabilities;

其中,request字段是一个布尔值,指示了信息的接收者是否需要回复一个 VD_AGENT_ANNOUNCE_CAPABILITIES 的信息,因为发送者可能也想知道接收者的capabilities。在最开始发起capabilities交换时这个应该设置为true,当发送一个宣告capabilities作为对所接收到的信息的应答时,该值设置为false。

结构体中的caps成员是一个可变长度数组的首地址。该数组的长度可以使用VDAgentMessage结构体中的size成员上的VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE 宏来确定。不同capabilities 的索引位于定义在VD_AGENT_CAP 常量的枚举中。并且有VD_AGENT_HAS_CAPABILITY和VD_AGENT_SET_CAPABILITY宏来测试/设置数组中的capability 位。

一方不应该发送另一方不支持的功能相关联的类型的信息。旧版本不支持announce capabilities,因此,直到收到announce capabilities信息,应该假设以下功能(所有版本都支持):

VD_AGENT_CAP_MOUSE_STATE
VD_AGENT_CAP_MONITORS_CONFIG
VD_AGENT_CAP_REPLY

VD_AGENT_CAP_CLIPBOARD_SELECTION

当客户端和服务器都具有selection capability,VDAgentClipboard信息必须在前面加上一个uint8_t,该uint8_t指示哪个 clipboard selection 可以操作+3个字节填充以供未来功能或扩展使用。

根据X11 / Gtk方案定义了几个剪贴板选择:

  • VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD: 默认剪贴板,由大多数操作系统实现,以处理显式的复制/粘贴操作。
  • VD_AGENT_CLIPBOARD_SELECTION_PRIMARY: PRIMARY剪贴板,用于鼠标选择。
  • VD_AGENT_CLIPBOARD_SELECTION_SECONDARY: the SECONDARY clipboard.

© 著作权归作者所有

Amui
粉丝 5
博文 78
码字总数 40874
作品 0
广州
程序员
私信 提问
openstack-mitaka多节点安装

最近学习了openstack环境搭建,并手动尝试成功搭建多节点mitaka版本。我是根据官网的安装手册指引操作的。完成过程中把它翻译成中文了,翻译或者理解有误的地方请多指教。 openstack-mitaka...

JennerLuo
2016/08/11
211
1
技术文档方案 | GitHub + Markdown 的深度实践解析

Foreword 最近在《GitHub + Markdown 的新轻型技术写作模式速览》中带大家快速了解了一下 GitHub + Markdown 这种比较新的技术写作模式。不出所料,确实有些小伙伴在读了上一篇分享后存在诸多...

Lilian_Lee
2018/08/22
0
0
强化学习实战——OpenAI Spinning Up项目

前面的文章介绍了太多强化学习的理论,今天我们就通过具体项目,理论结合实践深入理解和学习强化学习。 1. Spinning Up 项目简介 Spinning Up 是大名鼎鼎的Open AI推出的免费强化学习教学项目...

Hongtao洪滔
02/22
0
0
在KVM虚拟机中使用spice

1.spice介绍 1.1 spice简介 spice是由Qumranet开发的开源网络协议,2008年红帽收购了Qumranet获得了这个协议。SPICE是红帽在虚拟化领域除了KVM的又一“新兴技术”,它提供与虚拟桌面设备的远...

t_huanghai
2015/11/26
0
0
ZStack v1.0 正式版发布

ZStack 1.0版本今天发布,欢迎大家下载试用。在该版本中,我们加入了以下新功能: 新增功能 Flat Network Provider,分布式DHCP User Data,支持cloud-init 本地存储的磁盘迁移 ISO API,支持...

oschina
2016/02/02
2.5K
5

没有更多内容

加载失败,请刷新页面

加载更多

springboot 403 问题

添加WebAppConfigurer 配置 @Configuration@EnableAutoConfigurationpublic class WebAppConfigurer extends WebMvcConfigurerAdapter { public WebAppConfigurer() { } ......

布袋和尚_爱吃鱼
9分钟前
2
0
Python自动更换壁纸爬虫与tkinter结合

直接上代码 import ctypesimport timeimport requestsimport osfrom threading import Threadfrom tkinter import Tk, Label, Button,Entry,StringVar,messagebox# '放到AppData\Roami......

物种起源-达尔文
9分钟前
1
0
Postgresql Study 笔记

Postgresql 安装 Windows, MAC Install Postgresql 下载地址: https://www.enterprisedb.com/downloads/postgres-postgresql-downloads Linux Install sudo apt-get update sudo apt-get in......

slagga
11分钟前
2
0
layer.open 打开新页面传参问题

如图所示,点击出售,把A页面的数据传到弹框上面,因为弹框比较复杂,所以使用引入一个新页面。 A.html a.js B.html b.js 1、第一种方案 sellInte: function (){ var obj = document.g...

木九天
14分钟前
2
0
沙龙报名 | 区块链数据服务技术应用实践

京东云是国内首家提供区块链数据在线分析服务产品的公司,也是行业内首家对区块链数据服务进行开源的公司。 本次沙龙是京东云BDS开源后,首次在深圳举办线下沙龙,我们将邀请京东云BDS团队核...

京东云技术新知
15分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部