项目背景
在掌握了 UDP Socket 编程的基础接口后,我们直接上手一个实战项目——简易英译汉翻译服务器。这个项目采用经典的 C/S 架构,基于 UDP 协议实现。核心逻辑很简单:客户端输入英文单词并发送,服务端查询内存中的字典文件,返回对应的中文释义;如果找不到,则提示未查到。
整体设计思路
1. 架构与通信
- 服务端:加载字典到内存,监听固定端口,接收请求,查表并回复。
- 客户端:获取用户输入,发送请求,打印结果。
- 协议:UDP。无连接特性意味着不需要维护复杂的会话状态,服务端可以并发响应多个客户端的请求。
2. 核心模块划分
为了代码的可维护性,我们将功能拆分为几个独立模块:
- Socket 通信:封装底层的 socket、bind、recvfrom、sendto 操作。
- 字典管理:读取 txt 文件,解析键值对,存入哈希表(unordered_map)以便 O(1) 时间复杂度查询。
- 业务处理:通过回调函数解耦网络层和业务层。UdpServer 类只负责收发包,具体的查词逻辑由外部传入的函数决定。
3. 数据格式
字典文件使用纯文本格式,每行一个单词映射,例如 apple: 苹果。冒号加空格作为分隔符,方便后续按字符串分割处理。
关键技术点
哈希表加速查询
服务端启动时会一次性将字典文件读入内存。使用 C++ STL 的 unordered_map 存储 key-value 对,相比线性查找,查询效率有质的提升。对于高频查询场景,这是必要的优化手段。
类的封装与解耦
项目中定义了两个主要类:
- Dict 类:专门负责字典的加载和查询,对外暴露
Translate()接口。 - UdpServer 类:封装 UDP 服务端的生命周期(创建、绑定、循环监听)。它不关心具体业务,而是通过
std::function<void(const string&, string*)>类型的回调函数来执行业务逻辑。这种设计让 UdpServer 变得通用,未来只需更换回调函数即可适配不同的业务需求。
完整代码实现
1. 字典文件 (dict.txt)
确保与服务端程序在同一目录下,内容示例如下:
apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
happy: 快乐的
sad: 悲伤的
hello: 你好
world: 世界
Unknown: 未查到
2. 服务端代码 (dict_server.cpp)
这里整合了 Dict 类和 UdpServer 类。注意 HandleRequest 函数被注册为回调,静态对象 static Dict dict 保证了字典只在第一次调用时加载一次。
std;
DEFAULT_PORT = ;
DEFAULT_BUFF_SIZE = ;
string DICT_PATH = ;
string SEP = ;
string UNKNOWN = ;
{
:
unordered_map<string, string> _dict;
{
;
(!in.()) {
cerr << << (errno) << endl;
;
}
string line;
((in, line)) {
(line.()) ;
pos = line.(SEP);
(pos == string::npos) ;
string key = line.(, pos);
string value = line.(pos + SEP.());
_dict.((key, value));
}
in.();
cout << << _dict.() << << endl;
}
:
() { (); }
{
iter = _dict.(key);
(iter == _dict.()) {
UNKNOWN;
}
iter->second;
}
};
{
:
_sockfd;
_port;
function<( string&, string*)> _func;
{
_sockfd = (AF_INET, SOCK_DGRAM, );
(_sockfd < ) {
cerr << << (errno) << endl;
;
}
local;
(&local, , (local));
local.sin_family = AF_INET;
local.sin_port = (_port);
local.sin_addr.s_addr = INADDR_ANY;
((_sockfd, ( sockaddr*)&local, (local)) < ) {
cerr << << (errno) << endl;
(_sockfd);
;
}
cout << << _port << endl;
;
}
:
( port = DEFAULT_PORT, function<( string&, string*)> func = )
: _port(port), _func(func), _sockfd() {}
~() {
(_sockfd >= ) {
(_sockfd);
}
}
{
(!() || _func == ) {
cerr << << endl;
;
}
buffer[DEFAULT_BUFF_SIZE] = {};
() {
peer;
peer_len = (peer);
n = (_sockfd, buffer, (buffer), , ( sockaddr*)&peer, &peer_len);
(n > ) {
buffer[n] = ;
string req = buffer;
string resp;
_func(req, &resp);
(_sockfd, resp.(), resp.(), , ( sockaddr*)&peer, peer_len);
string peer_ip = (peer.sin_addr);
peer_port = (peer.sin_port);
cout << << peer_ip << << peer_port << << req << << resp << endl;
}
}
}
};
{
Dict dict;
*resp = dict.(req);
}
{
port = DEFAULT_PORT;
(argc == ) {
port = (argv[]);
}
;
server.();
;
}

