深入解析:【Linux网络】Socket编程:UDP网络编程实现DictServer

news/2025/10/12 13:04:36/文章来源:https://www.cnblogs.com/lxjshuju/p/19136514

上篇文章中我们实现了一个简单的网络通信EchoServer,客户端给服务端发送一条消息,服务端接收后再转发给客户端,最后客户端接收后回显在控制台上。
那么这篇文章呢,我们就把客户端发来的信息当作英文单词,服务端翻译成中文再转发回去,以此来实现一个英译汉的网络字典。

文章目录

  • 1. 网络通信部分
  • 2. 字典类
    • 2.1 框架
    • 2.2 加载字典
    • 2.3 翻译
  • 3. Udpserver.cc
  • 4. 封装InetAddr类

1. 网络通信部分

首先我们网络通信不需要改变,只需要稍微修改添加一些新的变量,服务端在接收客户端发来的数据,然后回调去处理翻译这个动作,所以我们可以使用包装器function来包装一个函数指针,用于回调处理翻译

代码如下:

#pragma once
#include <iostream>#include <string>#include <functional>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include "Log.hpp"using namespace LogModule;using func_t = std::function<std::string(const std::string&)>;class UdpServer{public:UdpServer(uint16_t port, func_t func):_socketfd(-1), _port(port), _isrunning(false), _func(func){}void Init(){_socketfd = socket(AF_INET, SOCK_DGRAM, 0);if(_socketfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(1);}LOG(LogLevel::INFO) << "socket success, socketfd: " << _socketfd;// 填充sockaddr_in结构体struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);//local.sin_addr.s_addr = inet_addr(_ip.c_str()); // TODOlocal.sin_addr.s_addr = INADDR_ANY;// 绑定IPv4地址结构int n = bind(_socketfd, (struct sockaddr*)&local, sizeof(local));if(n < 0){LOG(LogLevel::FATAL) << "bind error";exit(2);}LOG(LogLevel::INFO) << "bind success, sockfd : " << _socketfd;}void Start(){_isrunning = true;while(_isrunning){char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_socketfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if(n > 0){// 服务端需要知道客户端的ip和端口号uint16_t peer_port = ntohs(peer.sin_port); // 从网络中拿到的数据std::string peer_ip = inet_ntoa(peer.sin_addr); // 网络字节序转点分十进制buffer[n] = 0;// 将收到的数据,当作英语单词进行回调处理、std::string result = _func(buffer);// LOG(LogLevel::DEBUG) << "[" << peer_ip << ":" << peer_port<< "]# " << buffer; // 客户端发送的消息内容// 转发回去//std::string result = "Server echo# ";//result += buffer;ssize_t m = sendto(_socketfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);if(n < 0){LOG(LogLevel::FATAL) << "sendto error";exit(3);}}}}~UdpServer() {}private:int _socketfd;// std::string _ip; // 用的是字符串风格,点分十进制uint16_t _port; // 端口号bool _isrunning;func_t _func;};

2. 字典类

2.1 框架

#pragma once
#include <iostream>#include <unordered_map>#include "Log.hpp"using namespace LogModule;const std::string defaultdict = "./dictionary.txt";class Dict{public:Dict(const std::string& path = defaultdict):_dict_path(path){}// 加载预先准备好的字典bool LoadDict(){}// 翻译std::string Translate(std::string& word){}~Dict(){}private:std::unordered_map<std::string, std::string> _dict;std::string _dict_path; // 路径 + 文件名};

这里我们可以使用键值对的方式来查询英文单词对应的中文,并且我们可以预先将准备好的字典存入文件中,然后在翻译前将文件中的字典全部加载到哈希表中

apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
hello:
: 你好
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天

这里我们准备了一些单词和对应的中文,同时也增加了一些错误的格式,我们再加载的时候要注意处理

2.2 加载字典

由于我们预加载时,需要将每一行的英文单词和中文插入到哈希表中,所以我们需要将字符串分割,那我们为了方便,如下处理:

const std::string sep = ": ";

直接将在文件中准备好的字典加载到哈希表中,对于错误格式的单词,我们打印一条日志后不做处理,继续加载后续单词

// 加载预先准备好的字典
bool LoadDict()
{
std::fstream in(_dict_path);
if(!in.is_open())
{
LOG(LogLevel::ERROR) << "打开字典" << _dict_path << "失败";
return false;
}
std::string line;
while(std::getline(in, line))
{
// english: chinese
auto pos = line.find(sep);
if(pos == std::string::npos)
{
LOG(LogLevel::WARNING) << "解析: " << line << "失败";
continue; // 解析失败就跳过这个继续加载后续单词
}
std::string english = line.substr(0, pos);
std::string chinese = line.substr(pos + sep.size());
if(english.empty() || chinese.empty())
{
LOG(LogLevel::WARNING) << "没有有效内容: " << line;
continue;
}
_dict.insert(std::make_pair(english, chinese));
LOG(LogLevel::DEBUG) << "加载: " << line;
}
in.close();
}

2.3 翻译

翻译还是比较简单的,在哈希表中查找,如果英文单词不存在就返回字符串"None",存在就返回中文

// 翻译
std::string Translate(const std::string& word)
{
auto iter = _dict.find(word);
if(iter == _dict.end())
{
LOG(LogLevel::DEBUG) << "进入到了翻译模块: " << word << "->None";return "None";}LOG(LogLevel::DEBUG) << "进入到了翻译模块: " << word << "->" << iter->second;return iter->second;}

3. Udpserver.cc

服务端主程序已经有网络通信的功能了,我们现在只需要实例化字典对象,先加载字典到哈希表中,再在网络通信时进行翻译

代码如下:

#include <memory>#include "UdpServer.hpp"#include "Dict.hpp"// ./udpserver portint main(int argc, char* argv[]){if(argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}uint16_t port = std::stoi(argv[1]);Enable_Console_Log_Strategy();// 字典对象提供翻译功能Dict dict;dict.LoadDict();// 网络服务器对象提供网络通信功能std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port, [&dict](const std::string& word)->std::string{return dict.Translate(word);});usvr->Init();usvr->Start();return 0;}

客户端不需要动,代码如下:

#include <iostream>#include <string>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include "Log.hpp"using namespace LogModule;// ./udpclient server_ip server_portint main(int argc, char* argv[]){// 客户端需要绑定服务器的ip和portif(argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}Enable_Console_Log_Strategy();std::string server_ip = argv[1];uint16_t server_port = std::stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){LOG(LogLevel::FATAL) << "socket error";return 2;}// 不需要手动绑定ip和端口,操作系统会分配一个临时端口与ip进行绑定// 填写服务器信息struct sockaddr_in server;memset(&server, 0, sizeof(server)); // 这里使用memsetserver.sin_family = AF_INET;server.sin_port = htons(server_port); // 转成网络字节序server.sin_addr.s_addr = inet_addr(server_ip.c_str()); // 字符串->网络字节序while(true){// 从键盘获取要发送的数据std::string input;std::cout << "Client Enter# ";std::getline(std::cin, input);// 发送数据给服务器ssize_t n = sendto(sockfd, input.c_str(), input.size(), 0, (struct sockaddr*)&server, sizeof(server));if(n < 0){LOG(LogLevel::FATAL) << "sendto error";return 3;}// 接收服务器转发回来的数据并回显在控制台上char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t m = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if(m > 0){buffer[m] = 0;std::cout << buffer << std::endl;}}return 0;}

运行测试一下:

在这里插入图片描述


4. 封装InetAddr类

如果有多个客户端访问服务端,进行单词翻译,但是我们并不能看到是哪个客户端发送的数据,所以我们这里可以将客户端信息也打印出来,方便我们知道是哪个客户端在发送数据

所以我们这里可以将客户端的ip地址和端口打印出来,那肯定还会需要字节序列转换,我们干脆将字节序列转换封装一个类

#pragma once
#include <iostream>#include <string>#include <sys/socket.h>#include <sys/types.h>#include <arpa/inet.h>#include <netinet/in.h>// 网络地址和主机地址之间进行转换的类class InetAddr{public:InetAddr(struct sockaddr_in &addr): _addr(addr){_port = ntohs(_addr.sin_port);   // 从网络中拿到的数据_ip = inet_ntoa(_addr.sin_addr); // 网络字节序转点分十进制}uint16_t Port(){return _port;}std::string Ip(){return _ip;}~InetAddr() {}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;};

注意我们是想要在进入翻译模块时,将客户端信息给打印出来查看

// 翻译
std::string Translate(const std::string& word, InetAddr& client)
{
auto iter = _dict.find(word);
if(iter == _dict.end())
{
LOG(LogLevel::DEBUG) << "进入到了翻译模块: " << "[" << client.Ip() << ":" << client.Port() << "]" << word << "->None";return "None";}LOG(LogLevel::DEBUG) << "进入到了翻译模块: " << "[" << client.Ip() << ":" << client.Port() << "]" << word << "->" << iter->second;return iter->second;}

那么在主程序调用翻译时就需要增加参数

// 网络服务器对象提供网络通信功能
std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port, [&dict](const std::string& word, InetAddr& client)->std::string{return dict.Translate(word, client);});

那么包装器也需要增加一个参数

using func_t = std::function<std::string(const std::string&, InetAddr&)>;

再来测试一下

在这里插入图片描述


这篇文章中我们将处理的结果数据转发给一个客户端,那我们可不可以把数据转发给多个客户端呢,让大家都能看到,那不就相当于一个聊天室,大家都可以看到转发的数据。所以我们下篇文章就来实现一个简易版的聊天室

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/935373.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025焊接变位机厂家权威推荐榜:高效稳定与精准操控口碑之选

2025焊接变位机厂家权威推荐榜:高效稳定与精准操控口碑之选在工业制造智能化转型的浪潮中,焊接变位机作为焊接自动化系统的核心装备,其性能直接影响着焊接质量与生产效率。随着2025年智能制造战略的深入推进,焊接变…

20232404zxy 2025-2026-1 《网络与系统攻防技术》实验一实验报告

一、实验内容 1. 实验任务 (1)直接修改pwn1的机器指令,将main函数中调用foo的指令改为调用getShell。 (2)利用foo函数中gets(无边界检查)的漏洞,构造输入字符串覆盖返回地址,触发getShell。 (3)注入自定义S…

重装系统后实况足球pes2021需要安装的文件

重装系统后实况足球pes2021需要安装的文件最近重装系统,pes2021无法运行,记录需要重新安装的运行环境: 1、directx11 2、VC_redist.x64,vc++2015-2022发行版文件64位 3、VC_redist.x86,vc++2015-2022发行版文件32…

2025年10月铝塑板厂家最新推荐排行榜,吉祥铝塑板,门头铝塑板,墙面铝塑板,干挂铝塑板,外墙铝塑板公司推荐

在当前装饰板材市场中,铝塑板凭借其兼具金属与塑料特性的优势,被广泛应用于建筑外墙、室内精装、商业标识等多个场景。然而,行业发展仍面临不少亟待解决的问题。从产品品质来看,部分厂家为降低成本,选用低纯度铝皮…

杂题 9 月份

P3157 [CQOI2011] 动态逆序对 很明显的分块暴力查询,也可以 cdq 分治做,我的分块思路好像和洛谷题解区不大一样,详见。 P13976 数列分块入门 1 挺简单的分块,甚至没测样例我就过掉了。 P1972 [SDOI2009] HH 的项链…

2025防水包胶连接器厂家权威推荐榜:密封防护与耐用品质深度

2025防水包胶连接器厂家权威推荐榜:密封防护与耐用品质深度在工业自动化、新能源汽车、户外电子设备等领域快速发展的今天,防水包胶连接器作为关键电子组件,其密封防护性能和耐用品质直接影响到整个设备的可靠性与寿…

大语言模型真的需要那么多层吗?

研究发现大型语言模型中70%的注意力头和20%的前馈网络可以被移除而几乎不影响上下文学习性能,这表明当前的大语言模型可能存在训练不足的问题。该研究通过分析OPT-66B模型的结构组成,揭示了模型规模与训练效率之间的…

k8s-网络

k8s-网络k8s网络1.同一node上pod通讯:Kubernetes为每个Pod分配一个唯一的集群内部IP地址(Pod IP) POD IP :Pod 的唯一网络标识,用于容器间直接通信。动态分配,Pod 删除后 IP 回收。​集群外无法直接访问​ 由 ​C…

2025年微滤机厂家最新权威推荐榜:高效过滤与技术创新口碑之

2025年微滤机厂家最新权威推荐榜:高效过滤与技术创新口碑之选行业背景深度解析在水处理行业快速发展的当下,微滤机作为重要的固液分离设备,在市政供水、工业废水处理、水产养殖等领域发挥着关键作用。随着环保要求的…

swing修改jbutton的文字颜色

swing修改jbutton的文字颜色通过修改前景色来实现: jButton.setForeground(Color.red);

上海寿衣厂家最新权威推荐榜:品质工艺与贴心服务口碑之选

上海寿衣厂家最新权威推荐榜:品质工艺与贴心服务口碑之选在传统文化与现代殡葬服务融合发展的背景下,上海寿衣行业呈现出专业化、品质化的发展趋势。作为人生最后一程的重要礼仪用品,寿衣不仅承载着对逝者的尊重,更…

2025上海经侦律师事务所权威推荐榜:专业实力与胜诉口碑深度

2025上海经侦律师事务所权威推荐榜:专业实力与胜诉口碑深度在当今复杂的经济环境中,经济犯罪案件呈现出专业化、隐蔽化、跨区域化的新特点。企业面临的经济侦查案件不仅涉及金额巨大,更关乎企业声誉和未来发展。经侦…

Docker从网络管理到容器优化 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

从零实现 VGG-16

博客地址:https://www.cnblogs.com/zylyehuo/参考视频:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】需要用到的库torch安装有问题可参考网上教程pip install torchprotobufpip install protobufmodel.p…

完整教程:vlan Tag 概念及题目

完整教程:vlan Tag 概念及题目2025-10-12 12:28 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importa…

WPF上位机入门教程

WPF上位机入门教程 顾名思义,是一个WPF上位机入门的教程,包含WPF基本控件使用方法、控件库、MVVM框架、IOC框架、日志、XML、串口、Modbus、PLC、SocketServer、SQLite、曲线绘制、文档导入导出等内容。 控件库Handy…

潘院士高瞻远瞩:三大趋势勾勒中国AI发展路径,元人文构想恰逢其时

潘院士高瞻远瞩:三大趋势勾勒中国AI发展路径,元人文构想恰逢其时 拜读潘云鹤院士在2025网易未来大会上的深刻洞见,深受启发。院士提出的三大趋势——专业大模型崛起、具身智能泛化深化、AI引领平台经济2.0——精准描…

2025家居MES厂家最新权威推荐榜:智能制造与高效管理深度

2025家居MES厂家最新权威推荐榜:智能制造与高效管理深度随着工业4.0时代的深入发展,家居制造业正迎来智能化转型的关键时期。制造执行系统(MES)作为连接企业计划层与控制层的核心枢纽,在家居行业的数字化进程中扮…

开源 C# 快速构建(七)通讯--串口

开源 C# 快速构建(七)通讯--串口pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&…