草庐IT

Spice Vdagent协议

Lee_1985 2023-03-28 原文
Spice Guest Agent是运行在Guest OS内部的增强spice协议性能的重要组件。主要实现了:鼠标客户端模式,自动调节分辨率,剪切板复制,文件拖拽等重要功能。如果没有dagent程序,那么spice协议体检是无法接受的。

vdgent程序

  • 在Linux上使用串口:/dev/virtio-ports/com.redhat.spice.0
  • 在Windows上使用串口:\\\\.\\Global\\com.redhat.spice.0
  • qemu启动参数: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

协议头

1.VDIChunkHeader

vdagent 通过virtio串行端口与spice 服务端连接,可以发送数据到spice服务器和spice客户端,也可以接收它们发过来的数据。VDIChunkHeader作为消息头。

typedef struct SPICE_ATTR_PACKED VDIChunkHeader { uint32_t port; //vdagent接收消息时:port是消息来源,dagent发送消息时:port是接收方 uint32_t size; //size是发送或接收到的数据的大小,包括可变数据部分的大小, size=sizeof(VDAgentMessage)+variable_data_len } VDIChunkHeader; 元素port的值:

enum { VDP_CLIENT_PORT = 1, VDP_SERVER_PORT, }; 当要发送的消息是对已接收消息的应答消息,port的值应与已接收到的消息中的port值一致。

当spice server收到一条消息时,会判断VDIChunkHeader中的port数据,来决定是将数据直接发给客户端还是自己进行处理(如果是无效的value则丢弃)。目前还没有来自Agent的消息是针对spice server的,因此所有port值为VDP_SERVER_PORT的消息都会被服务器默默的丢弃。

2.VDAgentMessage

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

typedef struct SPICE_ATTR_PACKED VDAgentMessage { uint32_t protocol; //固定值VD_AGENT_PROTOCOL uint32_t type; //消息类型,在一个enum中 uint64_t opaque; //opaque是消息类型的占位符,只需要将单个整数作为消息数据传递,对于具有更多数据的消息类型,opaque值始终设置为0. uint32_t size; // 可变长度数据的大小,值为:sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + variable_data_len. uint8_t data[0]; //可变长度数据的起始地址,可变长度数据的内容取决于数据类型,对于大多数消息,它是一种数据类型特定的结构体,如VDAgentMouseSate。 } VDAgentMessage; 消息类型:即VDAgentMessage的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, };

协议详细信息

1.鼠标模式(VD_AGENT_MOUSE_STATE)

spice server ---> vdagent

typedef struct SPICE_ATTR_PACKED VDAgentMouseState { uint32_t x; uint32_t y; uint32_t buttons; uint8_t display_id; } VDAgentMouseState; spice支持两种鼠标模式,server和client。
在server模式下,QEMU ps/2鼠标仿真用于向客户机(Guest)发送鼠标状态。鼠标进入客户端窗口时候,俘获鼠标,客户端将鼠标移动增量坐标发送到客户机(Guest)。效果极差。
在client模式下,鼠标在显示器中的坐标位置作为绝对值发送给客户机。这需要使用usb表仿真,或将它们发送给vdagent,vdagent将通知客户机操作系统鼠标位置(和按钮单击)。

当鼠标处于client模式时,spice server端通过agent协议VD_AGENT_MOUSE_STATE,发送VDAgentMouseState消息给vdagent,由vagent调整guest os的鼠标位置。

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

2.显示器配置(VD_AGENT_MONITORS_CONFIG)

spice client ---> vdagent

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; 当客户端以全屏自动配置模式运行时,客户端会将此消息发送给vdagent。此消息包含连接到客户端计算机的显示器的信息。收到此消息后,vdagent应重新配置来宾中qxl vga设备的输出,以尽可能匹配消息中的输出。

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

3.消息应答(VD_AGENT_REPLY)

vdagent ---> spice client

typedef struct SPICE_ATTR_PACKED VDAgentReply { uint32_t type; uint32_t error; } VDAgentReply; 其中:error值为: enum { VD_AGENT_SUCCESS = 1, VD_AGENT_ERROR, }; 该消息由vdagent发送给client,表示vdagent已经完成了对VD_AGENT_MONITORS_CONFIG 或者 VD_AGENT_DISPLAY_CONFIG消息的处理,以及处理成功还是失败。

4.通知自己支持的功能(VD_AGENT_ANNOUNCE_CAPABILITIES)

spice client ---> vdagent vdagent ---> spice client 这条协议由spice client和vdagent通知自己支持的功能。此消息的目的是允许不同版本的客户端和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 位。

5. 显示效果优化(VD_AGENT_DISPLAY_CONFIG)

spice client ---> vdagent

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), }; spice客户端发送给vdagent,来通知vdagent一些特殊性能的相关设置。客户端可以要求vdagent禁用客户机系统的的许多功能,如字体反混叠等,以提高性能。vdagent可以在这些方面做一些努力,尤其是因为大多数设置都是以windows为中心的。应该使用VD_AGENT_REPLY 返回成功状态,除非出现问题。

6.剪切板相关协议

客户机上的agent和客户端起着对称的作用:它们都可以声明拥有权(GRAB),释放拥有权(RELEASE),请求剪贴板数据(REQUEST)以及发送剪贴板数据。当剪贴板清空时,grab信息必须被释放(release)掉。

另一端在grab处于活动状态时可以请求数据,之后会收到对方回复的带有剪贴板信息的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。

VD_AGENT_CLIPBOARD
struct VDAgentClipboard { if VD_AGENT_CAP_CLIPBOARD_SELECTION capability uint8_t selection; uint8_t __reserved[3]; endif uint32_t type; //type值是剪贴板数据类型,即前面剪贴板数据枚举类型中的一个。 uint8_t data[0]; //可变长度数据的起始地址 }; VD_AGENT_CLIPBOARD 用于发送剪贴板数据。除非接收到VD_AGENT_CLIPBOARD_REQUEST 的数据请求,否则这个数据不会被发送,以避免浪费宽带。被传送的剪贴板数据通常来说都挺大的,在这种情况下,可以预期到,要传送的数据是要分割成多个VD_AGENT_MESSAGE发送的。

VD_AGENT_CLIPBOARD_REQUEST
struct VDAgentClipboardRequest { if VD_AGENT_CAP_CLIPBOARD_SELECTION capability uint8_t selection; uint8_t __reserved[3]; endif uint32_t 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]; }; 抓取剪贴板数据。任何抓取类型的数据请求都应该成功。

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

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

  • (1)VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD: 默认剪贴板,由大多数操作系统实现,以处理显式的复制/粘贴操作。
  • (2)VD_AGENT_CLIPBOARD_SELECTION_PRIMARY: PRIMARY剪贴板,用于鼠标选择。
  • (3)VD_AGENT_CLIPBOARD_SELECTION_SECONDARY: the SECONDARY clipboard.
以spice client ---> vdagent为例:

  • spice client发送VD_AGENT_CLIPBOARD_GRAB消息,告诉spice client数据已经更改。
  • vdagent受到VD_AGENT_CLIPBOARD_GRAB消息,将剪切板owner设为spice client,并发送VD_AGENT_CLIPBOARD_REQUEST消息给spice client
  • spice client收到D_AGENT_CLIPBOARD_REQUEST消息,发送回包,带:VDagentClipboard结构体数据
  • vdagent接收到DagentClipboard结构体,获取剪切板数据,更新guest os的剪切板

7.文件传输相关协议

spice协议支持把文件从spice client拖拽到虚拟机内部。但是不支持反向操作。

VD_AGENT_FILE_XFER_START
typedef struct SPICE_ATTR_PACKED VDAgentFileXferStartMessage { uint32_t id; uint8_t data[0]; } VDAgentFileXferStartMessage;

VD_AGENT_FILE_XFER_STATUS

typedef struct SPICE_ATTR_PACKED VDAgentFileXferStatusMessage { uint32_t id; uint32_t result; /* Used to send additional data for detailed error messages * to clients with VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS capability. * Type of data varies with the result: * result : data type (NULL if no data) * VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE : uint64_t * VD_AGENT_FILE_XFER_STATUS_SESSION_LOCKED : NULL * VD_AGENT_FILE_XFER_STATUS_VDAGENT_NOT_CONNECTED : NULL * VD_AGENT_FILE_XFER_STATUS_DISABLED : NULL */ uint8_t data[0]; } VDAgentFileXferStatusMessage;

VD_AGENT_FILE_XFER_DATA

typedef struct SPICE_ATTR_PACKED VDAgentFileXferDataMessage { uint32_t id; uint64_t size; uint8_t data[0]; } VDAgentFileXferDataMessage;

有关Spice Vdagent协议的更多相关文章

  1. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  2. ruby - HTTP POST 上的 SSL 错误(未知协议(protocol)) - 2

    尝试通过SSL连接到ImgurAPI时出现错误。这是代码和错误:API_URI=URI.parse('https://api.imgur.com')API_PUBLIC_KEY='Client-ID--'ENDPOINTS={:image=>'/3/image',:gallery=>'/3/gallery'}#Public:Uploadanimage##args-Theimagepathfortheimagetoupload#defupload(image_path)http=Net::HTTP.new(API_URI.host)http.use_ssl=truehttp.verify

  3. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  4. 网络实验之RIPV2协议(一) - 2

    一、RIPV2协议简介  RIP(RoutingInformationProtocol)路由协议是一种相对古老,在小型以及同介质网络中得到了广泛应用的一种路由协议。RIP采用距离向量算法,是一种距离向量协议。RIP-1是有类别路由协议(ClassfulRoutingProtocol),它只支持以广播方式发布协议报文。RIP-1的协议报文无法携带掩码信息,它只能识别A、B、C类这样的自然网段的路由,因此RIP-1不支持非连续子网(DiscontiguousSubnet)。RIP-2是一种无类别路由协议(ClasslessRoutingProtocol),支持路由标记,在路由策略中可根据路由标记对

  5. ruby - 协议(protocol)族不支持的地址族 - 2

    我尝试执行此页面中显示的ruby示例:http://tomayko.com/writings/unicorn-is-unix但我得到的只是echo.rb:9:in`bind':Addressfamilynotsupportedbyprotocolfamily-bind(2)(Errno::EAFNOSUPPORT)fromecho.rb:9:in`'有什么想法吗? 最佳答案 我遇到了同样的问题。只需将localhost更改为0.0.0.0:address=Socket.pack_sockaddr_in(4242,'0.0.0.0')

  6. ruby-on-rails - 在 Github 上 fork Ruby/Rails gem 的正确协议(protocol)/礼仪是什么,可以作为持续的并行 fork 进行维护? - 2

    最近我使用了一个由单个开发人员创建的不错的gem,它托管在Github上。在我的工作中,我不得不对它进行一些实质性的修改,添加一些改进。有些是特定于项目的,有些是特定于gem的,还有一些是独立的改进。对于特定于gem的改进(例如,错误修复),我fork了存储库,应用了修复,并提出了拉取请求。然后,然而,我注意到独立的改进有点属于原始gem的并行、持续的分支类别。更清楚地说,你以前见过它;我重写了原始gem的View以使用TwitterBootstrap框架。因此,我也将它推送到了Github,但是,当然,我没有提出拉取请求——相反,我更新了README以解释不同之处,并感谢gem的原作

  7. ruby-on-rails - OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=unknown 状态:未知协议(protocol) - 2

    我关注了很多关于这个问题的帖子,但没有一个对我有帮助。我正在尝试使用最简单的irb命令进行连接:require'open-uri'open('https://aristo4stu3.bgu.ac.il')奇怪的是,对于我尝试过的任何其他httpsuri,它都工作正常(即https://google.com)。出于调试目的,我什至尝试使用以下方法禁用SSL验证:OpenSSL::SSL::VERIFY_PEER=OpenSSL::SSL::VERIFY_NONE这似乎也没有帮助。我的设置是(在AWS上):$rvm-vrvm1.21.3(stable)byWayneE.Seguin,Mic

  8. ruby-on-rails - 如何使用rails输出当前协议(protocol)和url? - 2

    我有一个共享对话框。在开发中它看起来像:http://xxxxxx.com/stuff产品上:https://yyyyyyyyyyy.com/stuff输出#Protocol##URL#的最佳Rails方式是什么我可以在哪里设置它们?谢谢 最佳答案 request.protocolrequest.url参见requestobject. 关于ruby-on-rails-如何使用rails输出当前协议(protocol)和url?,我们在StackOverflow上找到一个类似的问题:

  9. 【计算机网络】wireshark基本操作及ARP协议分析 - 2

    实验一wireshark基本操作及ARP协议分析一、实验目的1、熟悉并掌握Wireshark的基本使用;2、了解网络协议实体间进行交互以及报文交换的情况;3、分析以太网帧,MAC地址和ARP协议。二、实验环境与因特网连接的计算机,操作系统为Windows,安装有Wireshark、IE等软件。三、预备知识(1)wireshark安装下载地址:https://www.wireshark.org/#download注意操作系统版本,特别是32位操作系统和64位操作系统的区别。安装时选择默认设置即可。(2)分组嗅探器要深入理解网络协议,需要观察它们的工作过程并使用它们,即观察两个协议实体之间交换的报

  10. javascript - 聊天使用什么协议(protocol)? webRTC 或 Websockets - 2

    我正在创建一个可以进行视频、音频和文本聊天的聊天网站。我目前正在使用Websockets进行文本聊天。我需要知道视频、音频和文本聊天的最佳方式是什么。哪个更有效。我应该为所有这些使用Websockets,还是为视频和音频使用webRTC,为文本聊天使用Websockets。我可以两者都做,并且不想为webRTC重新编写我的文本聊天,但如果这是一个更好的选择,那么我会的。编辑:我刚刚发现了binaryJS,它是webRTC更好更简单的替代品吗?谢谢,@_C1D 最佳答案 通过将您的视频和音频流嵌入到websockets中,您将拥有一个

随机推荐