YAML:C++配置文件如使用(socket为例)?
基础概念
定义
- 一种人类可读的数据序列化格式,语法简洁,适合配置文件和数据交换。
- 支持键值对、列表、嵌套结构,通过缩进表示层级关系。
示例语法
server: # 对象(字典)host: "127.0.0.1" # 字符串值port: 8080 # 整数值enabled: true # 布尔值protocols: # 列表- http- https
使用前提?(虚拟机Ubuntu下)
下载与安装
首先必须要下载yaml-cpp
下载终端指令
sudo apt install libyaml-cpp-dev
YAML和yaml-cpp关系
- YAML 是格式标准:定义了数据的表示方式(如键值对、列表、嵌套结构等)。
- yaml-cpp 是解析工具:是针对 C++ 语言实现的 YAML 解析库,类似的还有 Python 中的
PyYAML
、Java 中的SnakeYAML
等。 - 类比:YAML 相当于 “HTML 格式标准”,而 yaml-cpp 相当于 “C++ 中的 HTML 解析器库”。
如何使用?
创建后缀为yaml的config.yaml文件
server: # 顶层键(对象/字典)host: "127.0.0.1" # 子键:服务器IP地址port: 8080 # 子键:服务器端口号
-
server
是一个对象(Object),相当于 C++ 中的std::map
或 Python 中的dict
。 -
host
和port
是server
对象的属性,分别存储字符串和整数类型的值
在C++文件中提取配置的步骤
#include <yaml-cpp/yaml.h>
#include <iostream>int main() {// 1. 加载YAML文件YAML::Node config = YAML::LoadFile("config.yaml");// 2. 提取server节点(YAML::Node类型)YAML::Node serverNode = config["server"];// 3. 从server节点中提取host和portstd::string host = serverNode["host"].as<std::string>(); // 提取字符串int port = serverNode["port"].as<int>(); // 提取整数// 4. 打印结果(验证提取成功)std::cout << "服务器地址: " << host << std::endl;std::cout << "服务器端口: " << port << std::endl;return 0;
}
完整场景(socket套接字通信)
服务端yserver.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <yaml-cpp/yaml.h> // 添加yaml-cpp头文件
#include<iostream>
using namespace std;
#define BUFFER_SIZE 1024int main() {int server_socket, client_socket;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len;char buffer[BUFFER_SIZE];char host[16] = {0}; // 使用C风格字符串保持原有结构int port = 0;// 从YAML文件读取配置try {//1.加载YAML文件YAML::Node config = YAML::LoadFile("config.yaml");const std::string& host_str = config["server"]["host"].as<std::string>();strncpy(host, host_str.c_str(), sizeof(host) - 1);port = config["server"]["port"].as<int>();} catch (const YAML::Exception& e) {fprintf(stderr, "YAML解析错误: %s\n", e.what());return EXIT_FAILURE;}// 创建套接字server_socket = socket(AF_INET, SOCK_STREAM, 0);if (server_socket == -1) {perror("套接字创建失败");exit(EXIT_FAILURE);}// 设置服务器地址信息server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(host);server_addr.sin_port = htons(port);// 绑定地址和端口if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("Binding failed");exit(EXIT_FAILURE);}// 开始监听if (listen(server_socket, 5) == -1) {perror("Listring failed");exit(EXIT_FAILURE);}printf("Server listening on port %d\n", port);while (1) {// 接收客户端连接client_addr_len = sizeof(client_addr);client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);if (client_socket == -1) {perror("Accepting failed");exit(EXIT_FAILURE);}printf("Accepted connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));while (1) {// 接收数据int bytes_received = read(client_socket, buffer, BUFFER_SIZE);if (bytes_received <= 0) {printf("Client disconnected\n");close(client_socket);break;}buffer[bytes_received] = '\0';printf("接收的数据:%s", buffer);// 发送数据回客户端write(client_socket, buffer, bytes_received);}break;}cout<<"关闭套接字"<<endl;;close(server_socket);return 0;
}
客户端yclient.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <iostream>
#include <yaml-cpp/yaml.h> // 添加yaml-cpp头文件
using namespace std;
#define BUFFER_SIZE 1024int main(int argc, char* argv[]) {int client_socket;struct sockaddr_in server_addr;char buffer[BUFFER_SIZE];char host[16] = {0}; // 使用C风格字符串保持原有结构int port = 0;// 从YAML文件读取配置try {//读取并解析当前当前目录下的config.yaml文件YAML::Node config = YAML::LoadFile("config.yaml");/*config["server"]:访问根节点下的 server 键,返回 YAML::Node。["host"]:继续访问 server 节点下的 host 键。as<std::string>():将该节点的值转换为 C++ 字符串(std::string)。*/const std::string& host_str = config["server"]["host"].as<std::string>();//将 C++ 字符串安全地转换为 C 风格字符串,防止因字符串过长导致数组溢出,确保字符串以 \0 结尾strncpy(host, host_str.c_str(), sizeof(host) - 1);//提取port字段port = config["server"]["port"].as<int>();} catch (const YAML::Exception& e) {fprintf(stderr, "YAML解析错误: %s\n", e.what());exit(EXIT_FAILURE);}cout<<host<<" "<<port<<endl; // 创建套接字client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket == -1) {perror("套接字创建失败");exit(EXIT_FAILURE);}// 设置服务器地址信息server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0) {perror("无效地址");exit(EXIT_FAILURE);}// 连接服务器if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("连接失败");exit(EXIT_FAILURE);}printf("连接服务端 at %s:%d\n", host, port);while (1) {// 输入发送的数据printf("发送数据 (or 'exit' to quit): ");fgets(buffer, BUFFER_SIZE, stdin);if (strcmp(buffer, "exit\n") == 0) {break;}// 发送数据给服务器write(client_socket, buffer, strlen(buffer));// 接收回传数据int bytes_received = read(client_socket, buffer, BUFFER_SIZE);buffer[bytes_received] = '\0';printf("从服务端重新传回的数据: %s", buffer);}close(client_socket);return 0;
}
终端编译yserver.cpp和yclient.cpp
tcy@tcy-virtual-machine:~/sy2$ g++ yclient.cpp -o yclient -lyaml-cpp
tcy@tcy-virtual-machine:~/sy2$ g++ yserver.cpp -o yserver -lyaml-cpp
运行结果
YAML优势
对于包含大量参数的网络程序(如支持 SSL 的服务器、多客户端连接池),YAML 的分层结构可将配置按功能模块分组(如network
、security
、logging
),避免代码中出现冗长的参数列表。
YAML核心优势场景
场景 | 优势体现 |
---|---|
多人协作开发 | 配置文件易读,减少沟通成本 |
频繁变更的配置 | 无需编译即可修改,适合运维快速调整 |
复杂系统架构 | 分层配置清晰组织参数,避免代码混乱 |
多环境部署 | 不同环境配置隔离,降低部署错误风险 |
跨团队 / 跨语言协作 | 统一配置格式,减少集成障碍 |
对比其他配置格式的劣势
- 与 JSON 相比:YAML 语法更灵活,但解析速度略慢(实际应用中差异可忽略)。
- 与 INI 相比:YAML 支持复杂数据结构和分层配置,INI 仅支持简单键值对。
- 与环境变量相比:YAML 适合大量配置参数,环境变量适合少量敏感信息(如密码)。