协议头结构
#ifdef PHP_WIN32
#pragma pack(push)
#pragma pack(1)
#endif
typedef struct _yar_header {
unsigned int id;
unsigned short version;
unsigned int magic_num;
unsigned int reserved;
unsigned char provider[32];
unsigned char token[32];
unsigned int body_len;
}
#ifndef PHP_WIN32
__attribute__ ((packed))
#endif
yar_header_t;
#ifdef PHP_WIN32
#pragma pack(pop)
#endif
yar底层使用一个yar_header_t
描述载荷的协议头信息.
- id 即requestId,用来标识同一个来回的rpc数据。
- version为协议版本,yar以后可能会用于版本处理或兼容,目前没有任何用处。
- magic_num是魔数,恒定为0x80DFEC60。yar用来初步判断客户端发来的是不是符合yar协议的合法数据,以及大小端是否匹配。
- provider,token都是鉴权字段,类似于username和password
- body_len 载荷长度
- reserved 保留字段,目前也没有任何用途。
//yar_protocol.c
void php_yar_protocol_render(yar_header_t *header, uint id, char *provider, char *token, uint body_len, uint reserved) /* {{{ */ {
header->magic_num = htonl(YAR_PROTOCOL_MAGIC_NUM);
header->id = htonl(id);
header->body_len = htonl(body_len);
header->reserved = htonl(reserved);
if (provider) {
memcpy(header->provider, provider, strlen(provider));
}
if (token) {
memcpy(header->token, token, strlen(token));
}
return;
} /* }}} */
yar_header_t * php_yar_protocol_parse(char *payload) /* {{{ */ {
yar_header_t *header = (yar_header_t *)payload;
header->magic_num = ntohl(header->magic_num);
if (header->magic_num != YAR_PROTOCOL_MAGIC_NUM) {
header->magic_num = htonl(header->magic_num);
return NULL;
}
header->id = ntohl(header->id);
header->body_len = ntohl(header->body_len);
header->reserved = ntohl(header->reserved);
return header;
} /* }}} */
这是两个协议头关键函数,分别用户生成 网络序的yar_header_t
和通过传递过来的 二进制数据 解析出 主机序的yar_header_t
变量.
协议整体结构
协议数据的处理散落各个传输器中,我们看看最清晰的curl传输器的处理
int php_yar_curl_send(yar_transport_interface_t* self, yar_request_t *request, char **msg) /* {{{ */ {
yar_header_t header = {0};
yar_curl_data_t *data = (yar_curl_data_t *)self->data;
zend_string *payload;
//payload 包含了packager_name的序列化文本,见packager章节
if (!(payload = php_yar_request_pack(request, msg))) {
return 0;
}
DEBUG_C(ZEND_ULONG_FMT": pack request by '%.*s', result len '%ld', content: '%.32s'",
request->id, 7, ZSTR_VAL(payload), ZSTR_LEN(payload), ZSTR_VAL(payload) + 8);
//可以看到,yar_header_t->body_len描述的长度是整个payload的长度,即序列化文本加打包器名
php_yar_protocol_render(&header, request->id, data->host->user, data->host->pass, ZSTR_LEN(payload), 0);
//postfield是curl传输器存放post报文,也就是协议数据的地方
smart_str_appendl(&data->postfield, (char *)&header, sizeof(yar_header_t));
smart_str_appendl(&data->postfield, ZSTR_VAL(payload), ZSTR_LEN(payload));
zend_string_release(payload);
return 1;
} /* }}} */
可以看出,整个协议数据由yar_header_t和payload两部分构成,现在我们已经可以画出 Yar协议完整的数据整体结构图了:
首先是一个固定长度为82字节的协议头,我们刚刚介绍过了
协议的载荷有两个部分组成:
第一个部分是一个长度固定为8字节的 包装器名,描述了载荷第二部分的序列化方式。
第二部分就是序列化的正文,是一个Zval通过具体的Yar打包器生成的序列化数据,其长度为(yar_header_t->body_len-8)
字节。
Zval有两种:
1.IMP数组
,其前身yar_request_t
结构体,代表一个请求 -----详见Request章节。
2.ISROE数组
,其前身是yar_response_t
结构体,代表一个响应-----详见Response章节。