结合一个完整的报文,我们可以详细说明如何实现报文的构建、发送、接收和处理过程。以下是一个示例,假设我们要发送一个“存储服务器向跟踪服务器请求加入”的报文。
1. 定义报文结构
首先,定义报文的数据结构,包括包头和包体。
typedef struct {
uint64_t body_len; // 包体长度,8字节
uint8_t command; // 命令,1字节
uint8_t status; // 状态,1字节
// 其他可能的包头字段...
} PacketHeader;
typedef struct {
char version[50]; // 版本信息
char group_name[50]; // 组名
char host_name[100]; // 主机名
uint16_t port; // 端口号
time_t start_time; // 启动时间
time_t join_time; // 加入时间
// 其他可能的包体字段...
} JoinRequestBody;
typedef struct {
PacketHeader header;
JoinRequestBody body; // 包体内容根据命令不同而不同
} JoinRequestPacket;
2. 创建报文
创建一个函数来构建完整的报文。
void create_join_request_packet(JoinRequestPacket* packet, const char* version, const char* group_name, const char* host_name, uint16_t port, time_t start_time, time_t join_time) {
packet->header.body_len = sizeof(JoinRequestBody);
packet->header.command = 10; // 假设10是请求加入的命令码
packet->header.status = 0; // 初始化状态为0
strcpy(packet->body.version, version);
strcpy(packet->body.group_name, group_name);
strcpy(packet->body.host_name, host_name);
packet->body.port = htons(port); // 网络字节序
packet->body.start_time = start_time;
packet->body.join_time = join_time;
}
3. 序列化报文
编写函数将报文结构体序列化为字节流。
int serialize_packet(const JoinRequestPacket* packet, unsigned char** buffer) {
int packet_size = sizeof(packet->header) + sizeof(packet->body);
*buffer = (unsigned char*)malloc(packet_size);
memcpy(*buffer, &packet->header, sizeof(packet->header));
memcpy(*buffer + sizeof(packet->header), &packet->body, sizeof(packet->body));
return packet_size;
}
4. 发送报文
使用套接字将序列化后的报文发送出去。
int send_packet(int socket_fd, const JoinRequestPacket* packet) {
unsigned char* buffer;
int len = serialize_packet(packet, &buffer);
int sent_bytes = send(socket_fd, buffer, len, 0);
free(buffer);
return sent_bytes;
}
5. 接收报文
在服务器端,接收报文并进行反序列化。
int receive_packet(int socket_fd, JoinRequestPacket* packet) {
unsigned char buffer[1024];
int received_bytes = recv(socket_fd, buffer, sizeof(buffer), 0);
if (received_bytes > 0) {
memcpy(&packet->header, buffer, sizeof(packet->header));
memcpy(&packet->body, buffer + sizeof(packet->header), sizeof(packet->body));
// 将网络字节序转换回主机字节序,如果需要的话
packet->body.port = ntohs(packet->body.port);
}
return received_bytes;
}
6. 处理报文
根据报文的命令和状态字段处理报文。
void handle_packet(JoinRequestPacket* packet) {
switch (packet->header.command) {
case 10: // 处理加入请求
// 逻辑处理加入请求
break;
// 处理其他命令
// ...
}
}
7. 示例使用
int main() {
int socket_fd = create_socket(); // 假设已经创建并连接的套接字
JoinRequestPacket packet;
create_join_request_packet(&packet, "1.0", "group1", "hostname", 8080, time(NULL), time(NULL));
if (send_packet(socket_fd, &packet) < 0) {
// 错误处理
}
JoinRequestPacket received_packet;
if (receive_packet(socket_fd, &received_packet) < 0) {
// 错误处理
}
handle_packet(&received_packet);
close_socket(socket_fd); // 关闭套接字
return 0;
}
请注意,上述代码是一个示例,实际应用中需要根据具体的报文规约和应用需求进行调整。特别是错误处理、内存管理和网络字节序转换需要特别注意。