searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

spice协议解析

2024-10-11 10:17:25
14
0

Spice协议解析

----------------------ref

Protocol.h里定义了所有通道的公共部分

Enums.h里定义了消息的type

Message.h里定义了消息的格式

 

 

简介

Spice一共有11个通道,如附录1.1所示,在所有通道中,main通道必须是第一个建立的。客户端使用不同的端口号区分不同的通道,服务端使用同一个端口号。对于所有通道来说,有一些消息是公共的。常用的通道有:a).main通道负责连接建立 b). display channel 负责图像显示 c). inputs channel for 发送鼠标键盘指令 d). cursor channel 接收鼠标指针的位置 e). Playback channel 接收音频流 and f).Record channel 发送音频。Spice协议采用网络字节序。

连接建立

对于所有的通道,连接建立的过程都是相似的,每个通道都要单独建立连接。每个通道建立连接时,客户端首先发送client link message,其中包括通道的类型和属性;服务端收到消息后,回复一个server link message。在server link message中有一个public_key字段,用于spice认证。

 

 

2.1  连接建立时的公告报头

 

client link messageserver link message拥有相同的数据包头

1) SPICE MAGIC32位,固定值,当前版本中为REDQ

2) 主要版本号:32位,当服务端和客户端的主要版本号一致时,忽略次要版本号;

3) 次要版本号:32位,当服务端和客户端的主要版本号不一致时,使用次要版本号;

4) 数据包长度:32位,除去spice头部外数据包的长度,单位为字节。

2.2  client link

client link message的字段如下:

1) 会话id32位,主通道第一次建立连接时,该字段的值为0,服务端生成一个会话id,并在spice main init消息中传递给客户端。spice其他通道建立连接时,该字段值为服务端生成的会话id,通过该字段判断通道间是否属于一次会话;

2) 通道类型:8位,1为主通道,2display通道,3input通道,4cursor通道,5playback通道,6record通道;

3) 通道id8位,当一次会话有多个相同类型的通道时,使用通道id区分相同类型的通道;

4) 共有属性数量:32位,属性分为所有通道共有的和该通道专有属性两类;

5) 通道属性数量:32位,通道专有属性的数量;

6) 属性偏移量:32位,单位为字节,从会话id起(除去头部)到第一个属性的偏移量;

7) 共有属性:32位,每个属性占一位,set1not set0,比如有4个属性,第一个和最后一个set,则值为1001(二进制),即0x09spice的共有属性包括: AUTH_SELECTION, AUTH_SPICE, AUTH_SASL,MINI_HEADER

8) 通道属性:32位,其他同上。主通道的专有属性包括: SEMI_SEAMLESS_MIGRATE,NAME_AND_UUID,GENT_CONNECTED_TOKENS,SEAMLESS_MIGRATE

2.3  server link

server link message的字段如下:

 

1) 错误码:32位,对client link message的回复,错误码为0时,证明连接正确建立。

2) 公钥:1024位,服务端把公钥发送给客户端,客户端使用公钥对密码进行加密,并将结果在spice ticket消息中发送给服务端进行验证。

3) 属性字段的说明参考client link message

使用ticket消息进行认证

服务端和客户端拥有相同的密码,服务端根据密码生成公钥和私钥,将公钥通过server link message发送给客户端。客户端使用公钥对密码进行加密,并将加密结果通过client ticket发送给服务端。服务端将收到的密码使用私钥进行解密,并与原来的密码进行对比,如果验证通过,将验证通过的结果通过server ticket消息(通过时该消息字段值为0)告知给客户端。

公共报头

除了建立连接时的四种消息外(client link messageserver link messageticketLinkAuthMechanism)外,其他的消息使用一样公共头部,头部包括16位的消息类型和32位的数据包大小。其中type的取值各通道间是独立的(不同通道间,即使type值相同,可能并不是同一种消息)。

主通道

建立连接后,主通道的第一个消息必须是Server INIT字段里Supported mouse mode由虚拟机的配置文件指定,服务端鼠标模式为必选模式,客户端鼠标模式为可选模式,客户端鼠标模式可能需要spice agent组件的支持。客户端模式使用鼠标的绝对位置,服务端鼠标模式使用鼠标的相对位置,只在虚拟机窗口范围内移动,主通道负责鼠标模式的控制。

4.1  server init

 

1) 会话id32位,由服务器生成并发送给客户端,客户端在建立其他通道时,使用该会话id

2) 显示通道数量:32位,期望的显示通道的数量,不能为0

3) 支持的鼠标模式:32位,值0x01时为只支持服务端鼠标模式,0x03时支持两种模式;

4) 当前的鼠标模式:32位,01为服务端模式,02为客户端模式;

5) 当前时间:32位,当前服务器的时间,用于视频发送时画面和声音的同步。

4.2  attach channels

服务端发送INIT消息后,客户端使用ATTACH CHANNELS消息对其进行回复,该消息类型值为104

 

显示通道

服务端的显示通道在redworker.c里处理。qxl会将图像渲染的命令(guest系统指定的)传递给服务端,服务端通过渲染树,去除掉中重复的图像数据,并放到一个管道中(管道与客户端一一对应)。每次push pipe时,从管道尾部取出数据,在服务端进行压缩,并生成一个数据包,发送给客户端。客户端在channel_display.c里对显示通道进行处理,图像显示在canavs_base.c里。

客户端发送SPICE_MSGC_DISPLAY_INIT消息指定自己的图像缓冲区大小(单位像素)。客户端发送SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION指定压缩算法,值4quic算法。服务端发送SPICE_MSG_DISPLAY_INVAL_PALETTE来清空客户端的缓冲区,发送SPICE_MSG_DISPLAY_SURFACE_CREATE建立一个客户端显示区域,包括区域的分辨率和格式。

5.1  client init

client init  指定客户端图像缓冲区大小:

 

5.2  preferred compression

PREFERRED_COMPRESSION 指定客户端使用的图像压缩算法,4quic

 

5.3  surface create

surface create服务端发送,指定surface的大小分辨率格式。

 

5.4  draw copy

draw copy  负责将图像从guest拷贝到客户端

 

 

surface id32

RECT:矩阵,包含4个位置信息,左上右下,指定该draw copy消息源图像的位置。当存在rect时,该字段的4个位置信息为所有rect矩阵位置的边界。

spice cliptype0时没有,为1时有clipclip的若干个矩阵对整个大矩阵进行切割。

1时,会有一个rect[]的列表,包括rect的数量和若干个rect

地址:32位指针地址

Rect:源图像的大小,矩形矩阵,可能与上面的rect字段大小相等,不等时进行缩放。

rop_descriptor16位 光栅操作码

scale_mode8位,缩放模式

SpiceQMaskmask; 限制copy的区域

SpiceImage *src_bitmap; 包括图像描述符SpiceImageDescriptor {

    uint64_t id;

    uint8_t type;          //图像类型:0是位图 1quic

    uint8_t flags;           

    uint32_t width;

uint32_t height;}和不同类型的图像的数据结构

 

type字段可能的取值有:0位图,1 quic103 from cache typefrom cache时,从缓存中取出特定id值的图像。

Flag的可能取值有1 SPICE_IMAGE_FLAGS_CACHE_ME,显示该图像以idkey存储到缓存中,2SPICE_IMAGE_FLAGS_HIGH_BITS_SET 二进制表示的最高两位忽略

Spice使用图像缓存技术,将每个图像分配一个id,以哈希表的方式存储在缓存中。在服务端对使用LRU算法图像缓存进行维护。只能由服务端控制主动清除缓存,客户端没有相应的机制。

Spice图像的消息可以分为三大部分,第一部分是目的图像的位置,由一个rect和可能存在的若干cliprect的列表)构成;第二部分是操作符,使用指定的操作符对源图像和目的图像进行相应的操作;第三部分是源图像,可能是一个图像或者调色板,或者是从缓存中获得的数据,将源图像使用操作符操作后,转成surface的图像格式,显示到目标图像。

Spice图像的来源有三种类型,一是服务端的源图像,通过网络获取或者从缓存中获取;二是调色板,即指定的颜色或指定一幅图像的某个位置的颜色;三是客户端特定位置的图像。通常使用不同的光栅操作符将三者中的某几种进行操作,最后得到最终的图像。

 

 

 

 

4.1 保持连接

使用pingpong来保持连接。

4.2 服务器通知(错误处理

服务端使用SPICE_MSG_NOTIFY对客户端进行消息通告,通告有三种类型,ERROR,WARN,INFO

 

4.3 其他

其他的消息包括通道迁移,通道确认,关闭连接等。

服务端发送INIT消息后,客户端使用ATTACH CHANNELS消息对其进行回复。

 

 

 

Server nameserver uuid由虚拟机的xml指定。

 

客户端回复attach channelsmain init消息进行回复。

 

收到attach channels后,服务端可以使用channels_list通告客户端可用的通道。

 

 

 

附录:

1通道列表

enum {

    SPICE_CHANNEL_MAIN = 1,

    SPICE_CHANNEL_DISPLAY,

    SPICE_CHANNEL_INPUTS,

    SPICE_CHANNEL_CURSOR,

    SPICE_CHANNEL_PLAYBACK,

    SPICE_CHANNEL_RECORD,

    SPICE_CHANNEL_TUNNEL,

    SPICE_CHANNEL_SMARTCARD,

    SPICE_CHANNEL_USBREDIR,   

    SPICE_CHANNEL_PORT,

    SPICE_CHANNEL_WEBDAV,

 

    SPICE_END_CHANNEL

};

2连接建立

2.1  消息格式

reds.c 2277: reds_init_client_connection socket有关 新建一个RedLinkInfo对象

客户端发送RedLinkMess

typedefstruct SPICE_ATTR_PACKED SpiceLinkHeader {

uint32_t magic;

uint32_t major_version;                 //2

uint32_t minor_version;                 //2

uint32_t size;

} SpiceLinkHeader;

typedefstruct SPICE_ATTR_PACKED SpiceLinkMess {

    uint32_t connection_id;

    uint8_t channel_type;

    uint8_t channel_id;

    uint32_t num_common_caps;

    uint32_t num_channel_caps;

    uint32_t caps_offset;

} SpiceLinkMess;

 

服务端发送RedLinkReply

typedefstruct SPICE_ATTR_PACKED SpiceLinkHeader {

uint32_t magic;

    uint32_t major_version;                 //1

uint32_t minor_version;                 //0

uint32_t size;

} SpiceLinkHeader;

typedefstruct SPICE_ATTR_PACKED SpiceLinkReply {

    uint32_t error;              //SPICE_LINK_ERR_OK 0成功

    uint8_t pub_key[SPICE_TICKET_PUBKEY_BYTES];

    uint32_t num_common_caps;

    uint32_t num_channel_caps;

    uint32_t caps_offset;

} SpiceLinkReply;

2.2 通用属性

enum {

    SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION,

    SPICE_COMMON_CAP_AUTH_SPICE,

    SPICE_COMMON_CAP_AUTH_SASL,

    SPICE_COMMON_CAP_MINI_HEADER,

};

3 公共消息

3.1 实际使用的公共报头

typedefstruct SPICE_ATTR_PACKED SpiceMiniDataHeader {

    uint16_t type;//类型

    uint32_t size;//大小

} SpiceMiniDataHeader;

3.2 公共的消息类型Enums.h:422

// 服务器端

enum {

    SPICE_MSG_MIGRATE = 1,        //迁移

    SPICE_MSG_MIGRATE_DATA,

    SPICE_MSG_SET_ACK,           //确认机制,客户端回复SPICE_MSGC_ACK_SYNC

    SPICE_MSG_PING,             //保持连接, SPICE_MSGC_PONG,

    SPICE_MSG_WAIT_FOR_CHANNELS,     //收到消息后,客户端等待

    SPICE_MSG_DISCONNECTING,      //关闭连接SPICE_MSGC_DISCONNECTING,

    SPICE_MSG_NOTIFY,           //通知,包括错误处理,警告信息

    SPICE_MSG_LIST,              //告知客户端通道的列表

    SPICE_MSG_BASE_LAST = 100,     //公共消息的类型取值为1-100

};

//客户端上的

enum {

    SPICE_MSGC_ACK_SYNC = 1,

    SPICE_MSGC_ACK,

    SPICE_MSGC_PONG,

    SPICE_MSGC_MIGRATE_FLUSH_MARK,

    SPICE_MSGC_MIGRATE_DATA,

    SPICE_MSGC_DISCONNECTING,

};

3.3 错误处理(ERROR CODEError_code.h

#define SPICEC_ERROR_CODE_SUCCESS 0

#define SPICEC_ERROR_CODE_ERROR 1

#define SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED 2

#define SPICEC_ERROR_CODE_CONNECT_FAILED 3

#define SPICEC_ERROR_CODE_SOCKET_FAILED 4

#define SPICEC_ERROR_CODE_SEND_FAILED 5

#define SPICEC_ERROR_CODE_RECV_FAILED 6

#define SPICEC_ERROR_CODE_SSL_ERROR 7

#define SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY 8

#define SPICEC_ERROR_CODE_AGENT_TIMEOUT 9

#define SPICEC_ERROR_CODE_AGENT_ERROR 10

#define SPICEC_ERROR_CODE_VERSION_MISMATCH 11

#define SPICEC_ERROR_CODE_PERMISSION_DENIED 12

#define SPICEC_ERROR_CODE_INVALID_ARG 13

#define SPICEC_ERROR_CODE_CMD_LINE_ERROR 14

3.4 SpiceNotify

typedef struct SpiceMsgNotify {

    uint64_t time_stamp;

    uint32_t severity;

    uint32_t visibilty;

    uint32_t what;

    uint32_t message_len;

    uint8_t message[0];

} SpiceMsgNotify;

pedef enum SpiceNotifySeverity {

    SPICE_NOTIFY_SEVERITY_INFO,

    SPICE_NOTIFY_SEVERITY_WARN,

    SPICE_NOTIFY_SEVERITY_ERROR,

 

    SPICE_NOTIFY_SEVERITY_ENUM_END

} SpiceNotifySeverity;

 

typedef enum SpiceNotifyVisibility {

    SPICE_NOTIFY_VISIBILITY_LOW,

    SPICE_NOTIFY_VISIBILITY_MEDIUM,

    SPICE_NOTIFY_VISIBILITY_HIGH,

 

    SPICE_NOTIFY_VISIBILITY_ENUM_END

} SpiceNotifyVisibility;

4.4 SPICE_MSG_LIST

4 Main Channel

4.1 主通道的专有属性

enum {

    SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE,

    SPICE_MAIN_CAP_NAME_AND_UUID,

    SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS,

    SPICE_MAIN_CAP_SEAMLESS_MIGRATE,

};

4.2 main通道消息类型

enum {

    SPICE_MSG_MAIN_MIGRATE_BEGIN = 101,

    SPICE_MSG_MAIN_MIGRATE_CANCEL,

    SPICE_MSG_MAIN_INIT,                             //建立第一个消息

    SPICE_MSG_MAIN_CHANNELS_LIST,                   //通道列表

    SPICE_MSG_MAIN_MOUSE_MODE,                   //鼠标模式

    SPICE_MSG_MAIN_MULTI_MEDIA_TIME,               //当没有playback通道时,使用此消息进行声音和画面的同步

    SPICE_MSG_MAIN_AGENT_CONNECTED,

    SPICE_MSG_MAIN_AGENT_DISCONNECTED,

    SPICE_MSG_MAIN_AGENT_DATA,

    SPICE_MSG_MAIN_AGENT_TOKEN,

    SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST,

    SPICE_MSG_MAIN_MIGRATE_END,

    SPICE_MSG_MAIN_NAME,                     //       虚拟机的名字

    SPICE_MSG_MAIN_UUID,                      //虚拟机的uuid

    SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS,

    SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS,

    SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK,

    SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK,

 

    SPICE_MSG_END_MAIN                       //结束主通道

};

 

enum {

    SPICE_MSGC_MAIN_CLIENT_INFO = 101,                  //客户端信息

    SPICE_MSGC_MAIN_MIGRATE_CONNECTED,

    SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR,

    SPICE_MSGC_MAIN_ATTACH_CHANNELS,                //init信息的回复

    SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST,           //

    SPICE_MSGC_MAIN_AGENT_START,

    SPICE_MSGC_MAIN_AGENT_DATA,

    SPICE_MSGC_MAIN_AGENT_TOKEN,

    SPICE_MSGC_MAIN_MIGRATE_END,

    SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS,

    SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS,

 

    SPICE_MSGC_END_MAIN                           

};

SPICE_MSG_MAIN_CHANNELS_LIST,

typedef struct SpiceMsgChannels {

    uint32_t num_of_channels;

    SpiceChannelId channels[0];

} SpiceMsgChannels;

SPICE_MSGC_MAIN_CLIENT_INFO

typedef struct SpiceMsgcClientInfo {

    uint64_t cache_size;

} SpiceMsgcClientInfo;

 

Red.c :1352

服务器通过一个回调函数接收数据包,接收到不同的数据包后,调用不同的函数进行回复。

**************reds_send_link_ack 发送RedLinkReply

 

5 显示通道

5.1 显示通道消息类型

服务端:

enum {

    SPICE_MSG_DISPLAY_MODE = 101,

    SPICE_MSG_DISPLAY_MARK,

    SPICE_MSG_DISPLAY_RESET,

    SPICE_MSG_DISPLAY_COPY_BITS,     //将指定区域复制到目标区域

    SPICE_MSG_DISPLAY_INVAL_LIST,

    SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,     //清除图像缓存,spice可以缓存调色板和图像

    SPICE_MSG_DISPLAY_INVAL_PALETTE,         //清除调色板

    SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES,    //清除所有调色板

    SPICE_MSG_DISPLAY_STREAM_CREATE = 122,   //stream开始

    SPICE_MSG_DISPLAY_STREAM_DATA,          //stream数据

    SPICE_MSG_DISPLAY_STREAM_CLIP,

    SPICE_MSG_DISPLAY_STREAM_DESTROY,

    SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL,

    SPICE_MSG_DISPLAY_DRAW_FILL = 302,       //指定区域绘制颜色

    SPICE_MSG_DISPLAY_DRAW_OPAQUE,        //将调色板和源图像结合

    SPICE_MSG_DISPLAY_DRAW_COPY,         //复制源图像到目标图像

    SPICE_MSG_DISPLAY_DRAW_BLEND,       //将源图像和目标图像混合

    SPICE_MSG_DISPLAY_DRAW_BLACKNESS,   //将目标区域变成黑色像素

    SPICE_MSG_DISPLAY_DRAW_WHITENESS,   //将目标区域变成白色像素

    SPICE_MSG_DISPLAY_DRAW_INVERS,     //翻转目标区域的像素

    SPICE_MSG_DISPLAY_DRAW_ROP3,      //将源图像、调色板和目标区域三者结合

    SPICE_MSG_DISPLAY_DRAW_STROKE,   //

    SPICE_MSG_DISPLAY_DRAW_TEXT,      //

    SPICE_MSG_DISPLAY_DRAW_TRANSPARENT,   //draw copy+指定颜色不显示

    SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND,   //使用alpha对源图像进行处理,复制到目标区域 

    SPICE_MSG_DISPLAY_SURFACE_CREATE,                 //生成一个surface

    SPICE_MSG_DISPLAY_SURFACE_DESTROY,                //销毁一个surface

    SPICE_MSG_DISPLAY_STREAM_DATA_SIZED,      //大量的stream数据

    SPICE_MSG_DISPLAY_MONITORS_CONFIG,       //显示屏的配置

    SPICE_MSG_DISPLAY_DRAW_COMPOSITE,

    SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT,

    SPICE_MSG_DISPLAY_GL_SCANOUT_UNIX,

    SPICE_MSG_DISPLAY_GL_DRAW,

 

    SPICE_MSG_END_DISPLAY

};

 

客户端:

enum {

    SPICE_MSGC_DISPLAY_INIT = 101,                           //指定图像缓冲区大小

    SPICE_MSGC_DISPLAY_STREAM_REPORT,

    SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION,                //指定压缩算法

    SPICE_MSGC_DISPLAY_GL_DRAW_DONE,

    SPICE_MSGC_END_DISPLAY

};

5.2 DRAW_COPY

[服务端发送red_worker.c:red_marshall_image  客户端解析canvas_base.c:canvas_draw_copy]

typedef struct SpiceMsgDisplayDrawCopy {

    SpiceMsgDisplayBase base;

    SpiceCopy data;

} SpiceMsgDisplayDrawCopy;

typedef struct SpiceMsgDisplayBase {

    uint32_t surface_id;

    SpiceRect box;

    SpiceClip clip;

} SpiceMsgDisplayBase;

typedef struct SpiceClip {

    uint8_t type;

    SpiceClipRects *rects;

} SpiceClip;

typedef struct SpiceCopy {

    SpiceImage *src_bitmap;

    SpiceRect src_area;

    uint16_t rop_descriptor;

    uint8_t scale_mode;

    SpiceQMask mask;

} SpiceCopy, SpiceBlend;

 

 

copy_bits:

把源区域的图像拷贝到目标区域,两个区域可能会重叠。指定矩形区域的左上角,矩形区域和目标区域的大小一样。

Draw_fill:

将固定的颜色填充到指定区域,颜色的格式和suface的格式一样,rgba各一个字节,16进制。

Draw_opaque:

将源图和调色板结合,渲染到目标区域

 

SPICE_ROPD_INVERS_SRC = (1 << 0),          //源图像取反
SPICE_ROPD_INVERS_BRUSH = (1 << 1),        //调色刷取反
SPICE_ROPD_INVERS_DEST = (1 << 2),         //目标区域渲染前取反
SPICE_ROPD_OP_PUT = (1 << 3),       //复制
SPICE_ROPD_OP_OR = (1 << 4),        //or
SPICE_ROPD_OP_AND = (1 << 5),      //and
SPICE_ROPD_OP_XOR = (1 << 6),      //xor
SPICE_ROPD_OP_BLACKNESS = (1 << 7),      
SPICE_ROPD_OP_WHITENESS = (1 << 8),
SPICE_ROPD_OP_INVERS = (1 << 9),      //目标像素取反
SPICE_ROPD_INVERS_RES = (1 << 10),    //结果取反

 

 

0条评论
作者已关闭评论
郑****理
4文章数
0粉丝数
郑****理
4 文章 | 0 粉丝
郑****理
4文章数
0粉丝数
郑****理
4 文章 | 0 粉丝
原创

spice协议解析

2024-10-11 10:17:25
14
0

Spice协议解析

----------------------ref

Protocol.h里定义了所有通道的公共部分

Enums.h里定义了消息的type

Message.h里定义了消息的格式

 

 

简介

Spice一共有11个通道,如附录1.1所示,在所有通道中,main通道必须是第一个建立的。客户端使用不同的端口号区分不同的通道,服务端使用同一个端口号。对于所有通道来说,有一些消息是公共的。常用的通道有:a).main通道负责连接建立 b). display channel 负责图像显示 c). inputs channel for 发送鼠标键盘指令 d). cursor channel 接收鼠标指针的位置 e). Playback channel 接收音频流 and f).Record channel 发送音频。Spice协议采用网络字节序。

连接建立

对于所有的通道,连接建立的过程都是相似的,每个通道都要单独建立连接。每个通道建立连接时,客户端首先发送client link message,其中包括通道的类型和属性;服务端收到消息后,回复一个server link message。在server link message中有一个public_key字段,用于spice认证。

 

 

2.1  连接建立时的公告报头

 

client link messageserver link message拥有相同的数据包头

1) SPICE MAGIC32位,固定值,当前版本中为REDQ

2) 主要版本号:32位,当服务端和客户端的主要版本号一致时,忽略次要版本号;

3) 次要版本号:32位,当服务端和客户端的主要版本号不一致时,使用次要版本号;

4) 数据包长度:32位,除去spice头部外数据包的长度,单位为字节。

2.2  client link

client link message的字段如下:

1) 会话id32位,主通道第一次建立连接时,该字段的值为0,服务端生成一个会话id,并在spice main init消息中传递给客户端。spice其他通道建立连接时,该字段值为服务端生成的会话id,通过该字段判断通道间是否属于一次会话;

2) 通道类型:8位,1为主通道,2display通道,3input通道,4cursor通道,5playback通道,6record通道;

3) 通道id8位,当一次会话有多个相同类型的通道时,使用通道id区分相同类型的通道;

4) 共有属性数量:32位,属性分为所有通道共有的和该通道专有属性两类;

5) 通道属性数量:32位,通道专有属性的数量;

6) 属性偏移量:32位,单位为字节,从会话id起(除去头部)到第一个属性的偏移量;

7) 共有属性:32位,每个属性占一位,set1not set0,比如有4个属性,第一个和最后一个set,则值为1001(二进制),即0x09spice的共有属性包括: AUTH_SELECTION, AUTH_SPICE, AUTH_SASL,MINI_HEADER

8) 通道属性:32位,其他同上。主通道的专有属性包括: SEMI_SEAMLESS_MIGRATE,NAME_AND_UUID,GENT_CONNECTED_TOKENS,SEAMLESS_MIGRATE

2.3  server link

server link message的字段如下:

 

1) 错误码:32位,对client link message的回复,错误码为0时,证明连接正确建立。

2) 公钥:1024位,服务端把公钥发送给客户端,客户端使用公钥对密码进行加密,并将结果在spice ticket消息中发送给服务端进行验证。

3) 属性字段的说明参考client link message

使用ticket消息进行认证

服务端和客户端拥有相同的密码,服务端根据密码生成公钥和私钥,将公钥通过server link message发送给客户端。客户端使用公钥对密码进行加密,并将加密结果通过client ticket发送给服务端。服务端将收到的密码使用私钥进行解密,并与原来的密码进行对比,如果验证通过,将验证通过的结果通过server ticket消息(通过时该消息字段值为0)告知给客户端。

公共报头

除了建立连接时的四种消息外(client link messageserver link messageticketLinkAuthMechanism)外,其他的消息使用一样公共头部,头部包括16位的消息类型和32位的数据包大小。其中type的取值各通道间是独立的(不同通道间,即使type值相同,可能并不是同一种消息)。

主通道

建立连接后,主通道的第一个消息必须是Server INIT字段里Supported mouse mode由虚拟机的配置文件指定,服务端鼠标模式为必选模式,客户端鼠标模式为可选模式,客户端鼠标模式可能需要spice agent组件的支持。客户端模式使用鼠标的绝对位置,服务端鼠标模式使用鼠标的相对位置,只在虚拟机窗口范围内移动,主通道负责鼠标模式的控制。

4.1  server init

 

1) 会话id32位,由服务器生成并发送给客户端,客户端在建立其他通道时,使用该会话id

2) 显示通道数量:32位,期望的显示通道的数量,不能为0

3) 支持的鼠标模式:32位,值0x01时为只支持服务端鼠标模式,0x03时支持两种模式;

4) 当前的鼠标模式:32位,01为服务端模式,02为客户端模式;

5) 当前时间:32位,当前服务器的时间,用于视频发送时画面和声音的同步。

4.2  attach channels

服务端发送INIT消息后,客户端使用ATTACH CHANNELS消息对其进行回复,该消息类型值为104

 

显示通道

服务端的显示通道在redworker.c里处理。qxl会将图像渲染的命令(guest系统指定的)传递给服务端,服务端通过渲染树,去除掉中重复的图像数据,并放到一个管道中(管道与客户端一一对应)。每次push pipe时,从管道尾部取出数据,在服务端进行压缩,并生成一个数据包,发送给客户端。客户端在channel_display.c里对显示通道进行处理,图像显示在canavs_base.c里。

客户端发送SPICE_MSGC_DISPLAY_INIT消息指定自己的图像缓冲区大小(单位像素)。客户端发送SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION指定压缩算法,值4quic算法。服务端发送SPICE_MSG_DISPLAY_INVAL_PALETTE来清空客户端的缓冲区,发送SPICE_MSG_DISPLAY_SURFACE_CREATE建立一个客户端显示区域,包括区域的分辨率和格式。

5.1  client init

client init  指定客户端图像缓冲区大小:

 

5.2  preferred compression

PREFERRED_COMPRESSION 指定客户端使用的图像压缩算法,4quic

 

5.3  surface create

surface create服务端发送,指定surface的大小分辨率格式。

 

5.4  draw copy

draw copy  负责将图像从guest拷贝到客户端

 

 

surface id32

RECT:矩阵,包含4个位置信息,左上右下,指定该draw copy消息源图像的位置。当存在rect时,该字段的4个位置信息为所有rect矩阵位置的边界。

spice cliptype0时没有,为1时有clipclip的若干个矩阵对整个大矩阵进行切割。

1时,会有一个rect[]的列表,包括rect的数量和若干个rect

地址:32位指针地址

Rect:源图像的大小,矩形矩阵,可能与上面的rect字段大小相等,不等时进行缩放。

rop_descriptor16位 光栅操作码

scale_mode8位,缩放模式

SpiceQMaskmask; 限制copy的区域

SpiceImage *src_bitmap; 包括图像描述符SpiceImageDescriptor {

    uint64_t id;

    uint8_t type;          //图像类型:0是位图 1quic

    uint8_t flags;           

    uint32_t width;

uint32_t height;}和不同类型的图像的数据结构

 

type字段可能的取值有:0位图,1 quic103 from cache typefrom cache时,从缓存中取出特定id值的图像。

Flag的可能取值有1 SPICE_IMAGE_FLAGS_CACHE_ME,显示该图像以idkey存储到缓存中,2SPICE_IMAGE_FLAGS_HIGH_BITS_SET 二进制表示的最高两位忽略

Spice使用图像缓存技术,将每个图像分配一个id,以哈希表的方式存储在缓存中。在服务端对使用LRU算法图像缓存进行维护。只能由服务端控制主动清除缓存,客户端没有相应的机制。

Spice图像的消息可以分为三大部分,第一部分是目的图像的位置,由一个rect和可能存在的若干cliprect的列表)构成;第二部分是操作符,使用指定的操作符对源图像和目的图像进行相应的操作;第三部分是源图像,可能是一个图像或者调色板,或者是从缓存中获得的数据,将源图像使用操作符操作后,转成surface的图像格式,显示到目标图像。

Spice图像的来源有三种类型,一是服务端的源图像,通过网络获取或者从缓存中获取;二是调色板,即指定的颜色或指定一幅图像的某个位置的颜色;三是客户端特定位置的图像。通常使用不同的光栅操作符将三者中的某几种进行操作,最后得到最终的图像。

 

 

 

 

4.1 保持连接

使用pingpong来保持连接。

4.2 服务器通知(错误处理

服务端使用SPICE_MSG_NOTIFY对客户端进行消息通告,通告有三种类型,ERROR,WARN,INFO

 

4.3 其他

其他的消息包括通道迁移,通道确认,关闭连接等。

服务端发送INIT消息后,客户端使用ATTACH CHANNELS消息对其进行回复。

 

 

 

Server nameserver uuid由虚拟机的xml指定。

 

客户端回复attach channelsmain init消息进行回复。

 

收到attach channels后,服务端可以使用channels_list通告客户端可用的通道。

 

 

 

附录:

1通道列表

enum {

    SPICE_CHANNEL_MAIN = 1,

    SPICE_CHANNEL_DISPLAY,

    SPICE_CHANNEL_INPUTS,

    SPICE_CHANNEL_CURSOR,

    SPICE_CHANNEL_PLAYBACK,

    SPICE_CHANNEL_RECORD,

    SPICE_CHANNEL_TUNNEL,

    SPICE_CHANNEL_SMARTCARD,

    SPICE_CHANNEL_USBREDIR,   

    SPICE_CHANNEL_PORT,

    SPICE_CHANNEL_WEBDAV,

 

    SPICE_END_CHANNEL

};

2连接建立

2.1  消息格式

reds.c 2277: reds_init_client_connection socket有关 新建一个RedLinkInfo对象

客户端发送RedLinkMess

typedefstruct SPICE_ATTR_PACKED SpiceLinkHeader {

uint32_t magic;

uint32_t major_version;                 //2

uint32_t minor_version;                 //2

uint32_t size;

} SpiceLinkHeader;

typedefstruct SPICE_ATTR_PACKED SpiceLinkMess {

    uint32_t connection_id;

    uint8_t channel_type;

    uint8_t channel_id;

    uint32_t num_common_caps;

    uint32_t num_channel_caps;

    uint32_t caps_offset;

} SpiceLinkMess;

 

服务端发送RedLinkReply

typedefstruct SPICE_ATTR_PACKED SpiceLinkHeader {

uint32_t magic;

    uint32_t major_version;                 //1

uint32_t minor_version;                 //0

uint32_t size;

} SpiceLinkHeader;

typedefstruct SPICE_ATTR_PACKED SpiceLinkReply {

    uint32_t error;              //SPICE_LINK_ERR_OK 0成功

    uint8_t pub_key[SPICE_TICKET_PUBKEY_BYTES];

    uint32_t num_common_caps;

    uint32_t num_channel_caps;

    uint32_t caps_offset;

} SpiceLinkReply;

2.2 通用属性

enum {

    SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION,

    SPICE_COMMON_CAP_AUTH_SPICE,

    SPICE_COMMON_CAP_AUTH_SASL,

    SPICE_COMMON_CAP_MINI_HEADER,

};

3 公共消息

3.1 实际使用的公共报头

typedefstruct SPICE_ATTR_PACKED SpiceMiniDataHeader {

    uint16_t type;//类型

    uint32_t size;//大小

} SpiceMiniDataHeader;

3.2 公共的消息类型Enums.h:422

// 服务器端

enum {

    SPICE_MSG_MIGRATE = 1,        //迁移

    SPICE_MSG_MIGRATE_DATA,

    SPICE_MSG_SET_ACK,           //确认机制,客户端回复SPICE_MSGC_ACK_SYNC

    SPICE_MSG_PING,             //保持连接, SPICE_MSGC_PONG,

    SPICE_MSG_WAIT_FOR_CHANNELS,     //收到消息后,客户端等待

    SPICE_MSG_DISCONNECTING,      //关闭连接SPICE_MSGC_DISCONNECTING,

    SPICE_MSG_NOTIFY,           //通知,包括错误处理,警告信息

    SPICE_MSG_LIST,              //告知客户端通道的列表

    SPICE_MSG_BASE_LAST = 100,     //公共消息的类型取值为1-100

};

//客户端上的

enum {

    SPICE_MSGC_ACK_SYNC = 1,

    SPICE_MSGC_ACK,

    SPICE_MSGC_PONG,

    SPICE_MSGC_MIGRATE_FLUSH_MARK,

    SPICE_MSGC_MIGRATE_DATA,

    SPICE_MSGC_DISCONNECTING,

};

3.3 错误处理(ERROR CODEError_code.h

#define SPICEC_ERROR_CODE_SUCCESS 0

#define SPICEC_ERROR_CODE_ERROR 1

#define SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED 2

#define SPICEC_ERROR_CODE_CONNECT_FAILED 3

#define SPICEC_ERROR_CODE_SOCKET_FAILED 4

#define SPICEC_ERROR_CODE_SEND_FAILED 5

#define SPICEC_ERROR_CODE_RECV_FAILED 6

#define SPICEC_ERROR_CODE_SSL_ERROR 7

#define SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY 8

#define SPICEC_ERROR_CODE_AGENT_TIMEOUT 9

#define SPICEC_ERROR_CODE_AGENT_ERROR 10

#define SPICEC_ERROR_CODE_VERSION_MISMATCH 11

#define SPICEC_ERROR_CODE_PERMISSION_DENIED 12

#define SPICEC_ERROR_CODE_INVALID_ARG 13

#define SPICEC_ERROR_CODE_CMD_LINE_ERROR 14

3.4 SpiceNotify

typedef struct SpiceMsgNotify {

    uint64_t time_stamp;

    uint32_t severity;

    uint32_t visibilty;

    uint32_t what;

    uint32_t message_len;

    uint8_t message[0];

} SpiceMsgNotify;

pedef enum SpiceNotifySeverity {

    SPICE_NOTIFY_SEVERITY_INFO,

    SPICE_NOTIFY_SEVERITY_WARN,

    SPICE_NOTIFY_SEVERITY_ERROR,

 

    SPICE_NOTIFY_SEVERITY_ENUM_END

} SpiceNotifySeverity;

 

typedef enum SpiceNotifyVisibility {

    SPICE_NOTIFY_VISIBILITY_LOW,

    SPICE_NOTIFY_VISIBILITY_MEDIUM,

    SPICE_NOTIFY_VISIBILITY_HIGH,

 

    SPICE_NOTIFY_VISIBILITY_ENUM_END

} SpiceNotifyVisibility;

4.4 SPICE_MSG_LIST

4 Main Channel

4.1 主通道的专有属性

enum {

    SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE,

    SPICE_MAIN_CAP_NAME_AND_UUID,

    SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS,

    SPICE_MAIN_CAP_SEAMLESS_MIGRATE,

};

4.2 main通道消息类型

enum {

    SPICE_MSG_MAIN_MIGRATE_BEGIN = 101,

    SPICE_MSG_MAIN_MIGRATE_CANCEL,

    SPICE_MSG_MAIN_INIT,                             //建立第一个消息

    SPICE_MSG_MAIN_CHANNELS_LIST,                   //通道列表

    SPICE_MSG_MAIN_MOUSE_MODE,                   //鼠标模式

    SPICE_MSG_MAIN_MULTI_MEDIA_TIME,               //当没有playback通道时,使用此消息进行声音和画面的同步

    SPICE_MSG_MAIN_AGENT_CONNECTED,

    SPICE_MSG_MAIN_AGENT_DISCONNECTED,

    SPICE_MSG_MAIN_AGENT_DATA,

    SPICE_MSG_MAIN_AGENT_TOKEN,

    SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST,

    SPICE_MSG_MAIN_MIGRATE_END,

    SPICE_MSG_MAIN_NAME,                     //       虚拟机的名字

    SPICE_MSG_MAIN_UUID,                      //虚拟机的uuid

    SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS,

    SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS,

    SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK,

    SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK,

 

    SPICE_MSG_END_MAIN                       //结束主通道

};

 

enum {

    SPICE_MSGC_MAIN_CLIENT_INFO = 101,                  //客户端信息

    SPICE_MSGC_MAIN_MIGRATE_CONNECTED,

    SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR,

    SPICE_MSGC_MAIN_ATTACH_CHANNELS,                //init信息的回复

    SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST,           //

    SPICE_MSGC_MAIN_AGENT_START,

    SPICE_MSGC_MAIN_AGENT_DATA,

    SPICE_MSGC_MAIN_AGENT_TOKEN,

    SPICE_MSGC_MAIN_MIGRATE_END,

    SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS,

    SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS,

 

    SPICE_MSGC_END_MAIN                           

};

SPICE_MSG_MAIN_CHANNELS_LIST,

typedef struct SpiceMsgChannels {

    uint32_t num_of_channels;

    SpiceChannelId channels[0];

} SpiceMsgChannels;

SPICE_MSGC_MAIN_CLIENT_INFO

typedef struct SpiceMsgcClientInfo {

    uint64_t cache_size;

} SpiceMsgcClientInfo;

 

Red.c :1352

服务器通过一个回调函数接收数据包,接收到不同的数据包后,调用不同的函数进行回复。

**************reds_send_link_ack 发送RedLinkReply

 

5 显示通道

5.1 显示通道消息类型

服务端:

enum {

    SPICE_MSG_DISPLAY_MODE = 101,

    SPICE_MSG_DISPLAY_MARK,

    SPICE_MSG_DISPLAY_RESET,

    SPICE_MSG_DISPLAY_COPY_BITS,     //将指定区域复制到目标区域

    SPICE_MSG_DISPLAY_INVAL_LIST,

    SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,     //清除图像缓存,spice可以缓存调色板和图像

    SPICE_MSG_DISPLAY_INVAL_PALETTE,         //清除调色板

    SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES,    //清除所有调色板

    SPICE_MSG_DISPLAY_STREAM_CREATE = 122,   //stream开始

    SPICE_MSG_DISPLAY_STREAM_DATA,          //stream数据

    SPICE_MSG_DISPLAY_STREAM_CLIP,

    SPICE_MSG_DISPLAY_STREAM_DESTROY,

    SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL,

    SPICE_MSG_DISPLAY_DRAW_FILL = 302,       //指定区域绘制颜色

    SPICE_MSG_DISPLAY_DRAW_OPAQUE,        //将调色板和源图像结合

    SPICE_MSG_DISPLAY_DRAW_COPY,         //复制源图像到目标图像

    SPICE_MSG_DISPLAY_DRAW_BLEND,       //将源图像和目标图像混合

    SPICE_MSG_DISPLAY_DRAW_BLACKNESS,   //将目标区域变成黑色像素

    SPICE_MSG_DISPLAY_DRAW_WHITENESS,   //将目标区域变成白色像素

    SPICE_MSG_DISPLAY_DRAW_INVERS,     //翻转目标区域的像素

    SPICE_MSG_DISPLAY_DRAW_ROP3,      //将源图像、调色板和目标区域三者结合

    SPICE_MSG_DISPLAY_DRAW_STROKE,   //

    SPICE_MSG_DISPLAY_DRAW_TEXT,      //

    SPICE_MSG_DISPLAY_DRAW_TRANSPARENT,   //draw copy+指定颜色不显示

    SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND,   //使用alpha对源图像进行处理,复制到目标区域 

    SPICE_MSG_DISPLAY_SURFACE_CREATE,                 //生成一个surface

    SPICE_MSG_DISPLAY_SURFACE_DESTROY,                //销毁一个surface

    SPICE_MSG_DISPLAY_STREAM_DATA_SIZED,      //大量的stream数据

    SPICE_MSG_DISPLAY_MONITORS_CONFIG,       //显示屏的配置

    SPICE_MSG_DISPLAY_DRAW_COMPOSITE,

    SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT,

    SPICE_MSG_DISPLAY_GL_SCANOUT_UNIX,

    SPICE_MSG_DISPLAY_GL_DRAW,

 

    SPICE_MSG_END_DISPLAY

};

 

客户端:

enum {

    SPICE_MSGC_DISPLAY_INIT = 101,                           //指定图像缓冲区大小

    SPICE_MSGC_DISPLAY_STREAM_REPORT,

    SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION,                //指定压缩算法

    SPICE_MSGC_DISPLAY_GL_DRAW_DONE,

    SPICE_MSGC_END_DISPLAY

};

5.2 DRAW_COPY

[服务端发送red_worker.c:red_marshall_image  客户端解析canvas_base.c:canvas_draw_copy]

typedef struct SpiceMsgDisplayDrawCopy {

    SpiceMsgDisplayBase base;

    SpiceCopy data;

} SpiceMsgDisplayDrawCopy;

typedef struct SpiceMsgDisplayBase {

    uint32_t surface_id;

    SpiceRect box;

    SpiceClip clip;

} SpiceMsgDisplayBase;

typedef struct SpiceClip {

    uint8_t type;

    SpiceClipRects *rects;

} SpiceClip;

typedef struct SpiceCopy {

    SpiceImage *src_bitmap;

    SpiceRect src_area;

    uint16_t rop_descriptor;

    uint8_t scale_mode;

    SpiceQMask mask;

} SpiceCopy, SpiceBlend;

 

 

copy_bits:

把源区域的图像拷贝到目标区域,两个区域可能会重叠。指定矩形区域的左上角,矩形区域和目标区域的大小一样。

Draw_fill:

将固定的颜色填充到指定区域,颜色的格式和suface的格式一样,rgba各一个字节,16进制。

Draw_opaque:

将源图和调色板结合,渲染到目标区域

 

SPICE_ROPD_INVERS_SRC = (1 << 0),          //源图像取反
SPICE_ROPD_INVERS_BRUSH = (1 << 1),        //调色刷取反
SPICE_ROPD_INVERS_DEST = (1 << 2),         //目标区域渲染前取反
SPICE_ROPD_OP_PUT = (1 << 3),       //复制
SPICE_ROPD_OP_OR = (1 << 4),        //or
SPICE_ROPD_OP_AND = (1 << 5),      //and
SPICE_ROPD_OP_XOR = (1 << 6),      //xor
SPICE_ROPD_OP_BLACKNESS = (1 << 7),      
SPICE_ROPD_OP_WHITENESS = (1 << 8),
SPICE_ROPD_OP_INVERS = (1 << 9),      //目标像素取反
SPICE_ROPD_INVERS_RES = (1 << 10),    //结果取反

 

 

文章来自个人专栏
ovs-dpdk卸载
4 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0