实现基于UDP简易的英汉词典

文章目录

  • 实现目标
  • 认识相关接口
    • socket
    • bzero
    • bind
    • recvfrom
    • sendto
  • 实现思路和注意事项
  • 完整代码
    • Server.hpp
    • Server.cc
    • Client.hpp
    • Client.cc
  • 运行效果
  • END

实现目标

  1. 实现一个服务端和一个客户端,客户端负责发送一个单词,服务端接收到后将翻译后的结果返回发送到客户端。
  2. 使用UDP网络连接,可以跨主机实现通信
  3. 服务端读取文件中保存的单词及其翻译,通过发送信号使服务端更新词库,不需要重启。

认识相关接口

socket

创建套接字文件,在Linux一切皆文件。。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);

参数一为需要选择的通信方式:

image-20230725153425886

通常是使用AF_UNIX AF_INET,分别表示为本地通信和网络通信

参数二为套接字提供服务的类型,通常使用SOCK_STREAM:流式服务TCP策略,SOCK_DGRAM:数据报服务,UDP策略

image-20230725153821653

参数三默认设为0即可,因为前面两个参数已经确定好通信的方式和策略

返回值:成功创建返回文件的文件描述符, 失败返回-1

 _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
assert(_sockfd != -1);
cout << "success : " << _sockfd << endl;

bzero

可以将结构体对象初始化,和memset同理

#include <strings.h>void bzero(void *s, size_t n);

bind

绑定端口号

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数一为:需要绑定的文件描述符

参数二为:sockaddr结构体对象的地址,通常使用sockaddr_in对象强转,这个结构体对象里面就包括了传输方式,端口号,和ip地址

参数三为:这个结构体对象的大小

成功返回0

assert(bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) == 0);

recvfrom

读取数据。

#include <sys/types.h>
#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

参数一为:文件描述符

参数二为:接收数据的存储对象

参数三为:接收数据的存储对象的大小

参数四默认为0,表示阻塞读取

参数五为:一个结构体对象,输入输出型参数,该对象接收到后里面包含了发送端的信息,以便在未来可以往这个位置发回信息。

参数六为:接收到这个结构体对象的大小

成功返回数据的字节数,失败返回-1

ssize_t s = recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);

sendto

发送数据

#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

参数一为:文件描述符

参数二为:发送的数据的缓冲区

参数三为:发送数据的长度

参数四默认为0,阻塞发送

参数五为:结构体对象,里面包含了接收端的属性,ip地址等

参数六为:结构体对象大小

sendto(sockfd, res.c_str(), res.size(), 0, (sockaddr *)&client, sizeof(client));

实现思路和注意事项

思路:

  1. 首先可以对客户端和服务端分别进行封装
  2. 两者都具有初始化,启动功能。初始化主要负责初始化自身的IP地址,端口号和通信方式等
  3. 两者的启动都必须要有发送和读取的功能,客户端先发送再读取,服务端先读取再发送
  4. 服务端要有一个接收到数据后的回调函数,对数据进行处理后再发送回去
  5. 使用C++文件操作,加载文件里的词库

注意事项:

  1. 运行服务端时必须带上端口号,运行客户端必须带上IP地址和端口号
  2. 服务端必须显示绑定端口号,客户端不需要。操作系统会帮客户端自动生产并绑定端口号,因为服务端是只有一个,而访问这个服务端的客户端却会有很多个。
  3. 服务端的IP地址不能够指定某个特定的IP地址,必须使用0.0.0.0,因为会有很多个客户端访问,如果指明一个特定的IP地址,那么就可能出现别的IP访问不了端口号
  4. 注意端口号必须要调用接口去转换一下大小端,因为很多情况下都不清楚机器的大小端,养成好习惯
  5. 所有接口的参数都是 sockaddr类型的结构体,但是在使用的时候往往都是定义 sockaddr_in 结构体,传参的时候再强转。sockaddr_in的属性分别为:sin_family 传输方式;sin_port 端口号;sin_addr.s_addr IP地址

完整代码

以下代码均有注释,上述不完整的代码的注释里都有解释

Server.hpp

#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cassert>
#include <unistd.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
using namespace std;typedef function<void(int, string, uint16_t, string)> func_t;
class udpServer
{
public:udpServer(const uint16_t &port, const func_t &funcCall): _port(port), _ip("0.0.0.0"), _funcCall(funcCall){}// 初始化服务器端void initServer(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);assert(_sockfd != -1);cout << "success : " << _sockfd << endl;// 定义socket_in结构体变量struct sockaddr_in local;// 初始化这个变量bzero(&local, sizeof(local));// 填充这个变量里的属性local.sin_family = AF_INET; // 指定传输方式// 指定端口号,不明确大小端所以要调用一下转换函数local.sin_port = htons(_port);// 指定IP地址, 首先要把字符串类型转换成网络IP的整型再转换大小端// 一般而言不会指明一个特定的IP地址,而是会设为0.0.0.0// 因为如果只绑定一个明确的IP,最终的数据可能用别的IP来访问端口号就会访问不了// INADDR_ANY就是0.0.0.0// local.sin_addr.s_addr = inet_addr(_ip.c_str());local.sin_addr.s_addr = INADDR_ANY;// 绑定端口号assert(bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) == 0);}// 启动服务器端void start(){char buff[1024];// 服务器本质就是一个死循环,除非紧急情况否则不退出while (1){struct sockaddr_in peer;// 保存这个结构体大小的变量socklen_t len = sizeof(peer);ssize_t s = recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);if (s > 0){// 记录数据是什么,哪个IP地址发的,发到哪个端口// 首先peer里的IP地址是网络序列,所以要转化为整形再转成点分制的字符串string clientip = inet_ntoa(peer.sin_addr);// 端口号也要利用函数调用转换为16位的整形uint16_t clientport = ntohs(peer.sin_port);// 保存数据buff[s] = 0;string message = buff;// 读取数据cout << clientip << "[ #: " << clientport << "] : " << message << endl;// 处理数据后再发回客户端_funcCall(_sockfd, clientip, clientport, message);}sleep(1);}}~udpServer(){}private:uint16_t _port; // 端口号string _ip;     // ip地址int _sockfd;    // 创建socket后的网络文件描述符func_t _funcCall; // 回调方法
};

Server.cc

#include "Server.hpp"
#include <memory>
#include <unordered_map>
#include <fstream>
#include <signal.h>#define textfile "./dict.txt"
// 保存字典
unordered_map<string, string> dict;// 输出命令错误函数
void Usage(string proc)
{cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";
}// 读取一行中的kv值
bool getString(const string &line, string *key, string *value)
{auto pos = line.find(":");if (pos == string::npos)return false;// 分割两段字符串 分别提取*key = line.substr(0, pos);*value = line.substr(pos + 1);return true;
}// 初始化字典
void Initdict()
{string key, value, line;// 打开文件读取内容插入到dict中ifstream ifs(textfile, ios::binary);if (!ifs.is_open()){cerr << "open file error" << endl;exit(3);}while (getline(ifs, line)){if (getString(line, &key, &value))dict.insert(make_pair(key, value));}ifs.close();cout << "dict success" << endl;
}// 如果收到2号信号则重新读取文件重新加载dict
void reload(int signal)
{Initdict();
}// 设置接收数据后的回调函数
void CallMessage(int sockfd, string clientip, uint16_t clientport, string message)
{// 对接收到的数据进行自定义处理// 与通信解耦// 查询接收到的单词并查找auto it = dict.find(message);string res;if (it == dict.end())res = "未查询到";elseres = it->second;// 将查询到的结果返回去struct sockaddr_in client;client.sin_family = AF_INET;client.sin_addr.s_addr = inet_addr(clientip.c_str());client.sin_port = htons(clientport);sendto(sockfd, res.c_str(), res.size(), 0, (sockaddr *)&client, sizeof(client));
}int main(int argc, char *argv[])
{// 从命令行获取命令// 其中包括端口号// 如果分割不为两部分就说明命令有误,输出错误信息后退出if (argc != 2){Usage(argv[0]);exit(2);}// 拿到端口号uint16_t port = atoi(argv[1]);// 如果收到2号信号则重新读取文件重新加载dictsignal(2, reload);// 初始化字典Initdict();unique_ptr<udpServer> us(new udpServer(port, CallMessage));us->initServer();us->start();return 0;
}

Client.hpp

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <strings.h>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
using namespace std;class udpClient
{
public:udpClient(const string &server_ip, const uint16_t &server_port): _server_ip(server_ip), _server_port(server_port){}void clientInit(){_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1)exit(2);cout << "success : " << _sockfd << endl;// 客户端也需要绑定IP地址和端口,但是不需要显示绑定,操作系统会自动绑定// 客户端的端口号对服务端而言并不重要,它只需要确定自己的唯一性即可// 相当于写服务器的是一家公司,写客户端的是无数家公司,无数家公司之间只需要不冲突即可}void run(){string buff;struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(_server_ip.c_str());server_addr.sin_port = htons(_server_port);while (1){cout << "Please cin:";cin >> buff;// sendto自动帮客户端绑定端口ssize_t s = sendto(_sockfd, buff.c_str(), buff.size(), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));// 接收服务端发回来的数据char message[1024];struct sockaddr_in temp;bzero(&temp, sizeof(temp));socklen_t len = sizeof(temp);size_t n = recvfrom(_sockfd, message, sizeof(message) - 1, 0, (struct sockaddr *)&temp, &len);if (n > 0)message[n] = 0;cout << "翻译结果:" << message << endl;}}~udpClient(){}private:int _sockfd;string _server_ip;uint16_t _server_port;
};

Client.cc

#include "Client.hpp"
#include <memory>// 输出命令错误函数
void Usage(string proc)
{cout << "Usage:\n\t" << proc << " server_ip server_port\n\n";
}int main(int argc, char* argv[])
{   // 从命令行获取命令// 其中包括服务端的IP地址和对应的端口号// 如果分割不为两部分就说明命令有误,输出错误信息后退出if(argc != 3){Usage(argv[0]);exit(2);}// 保存服务端的IP地址和端口号string server_ip = argv[1];uint16_t server_port = atoi(argv[2]);unique_ptr<udpClient> cs(new udpClient(server_ip, server_port));cs->clientInit();cs->run();return 0;
}

运行效果

初始词库

image-20230725161246600

运行效果:

更新后词库

image-20230725161612973

运行:不需要重启服务端,发送2号信号(ctrl + c)

END

以上就是本篇简易的UDP英汉词典了,期待各位佬们能够指点一二。

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

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

相关文章

Android 之 动画合集之帧动画

本节引言&#xff1a; 从本节开始我们来探究Android中的动画&#xff0c;毕竟在APP中添加上一些动画&#xff0c;会让我们的应用变得 很炫&#xff0c;比如最简单的关开Activity&#xff0c;当然自定义控件动画肯定必不可少啦~而Android中的动画 分为三大类&#xff0c;逐帧动画…

了解uuid

目录 一.认识 UUID 二.UUID 会耗尽吗 三.UUID 会重复吗 四.UUID 的版本 五.UUID的应用 六.java 如何生成UUID 一.认识 UUID uuid是经过特定的算法得到的. UUID 是 16 字节 128 位长的数字&#xff0c;通常以 36 字节的字符串表示&#xff0c;示例如下&#xff1a; 3F2…

boardmix AI:让工作效率翻倍的AI智能在线白板软件!

随着ChatGPT热度的飙升&#xff0c;AI逐步深入到各个领域&#xff0c;尤其在技术领域&#xff0c;引发了一场AI的新浪潮&#xff0c;人们谈论的焦点都与AI有关。 AI工具不仅帮助企业节约了成本&#xff0c;还极大提高了生产力。那些尚未融入AI的行业和产品&#xff0c;有着被AI…

OSI 和 TCP/IP 网络分层模型详解(基础)

OSI模型: 即开放式通信系统互联参考模型&#xff08;Open System Interconnection Reference Model&#xff09;&#xff0c;是国际标准化组织&#xff08;ISO&#xff09;提出的一个试图使各种计算机在世界范围内互连为网络的标准框架&#xff0c;简称OSI。 OSI 七层模型 OS…

Windows环境部署安装Chatglm2-6B-int4

chatglm2-6B是最近比较火爆的大模型&#xff0c;可以在消费级显卡上部署使用&#xff0c;适合学习。但是一般人也不一定有那么高的硬件配置&#xff0c;所以部署个int4版本应该是大多数人的最好选择。我就在家里部署起了int4版本的chatglm2-6B&#xff0c;记录一下免得忘了。 …

【AST抽象语法树】结构分析及特性

什么是AST? AST译名抽象语法树&#xff08;Abstract Syntax Tree&#xff09;&#xff0c;是一种用于表示源代码结构的数据结构。 它在编译器、解析器和静态代码分析等领域中被广泛使用。 AST结构分析 我们利用成熟的astexplorer来进行结构化的比较和分析。可以尝试登录以下网…

树 - 前缀树(Trie Tree)

树 - 前缀树(Trie Tree&#xff09; 什么是前缀树前缀树的实现节点数据结构定义插入方法●非递归方式●递归方式 查询单词方法●非递归方式●递归方式 查询前缀方法●非递归方式●递归方式 前缀树的复杂度前缀树有哪些应用前缀树的压缩&#xff1a;基数树双数组Trie树(DoubleAr…

探讨ChatGPT的强化学习:AI学习与交互的未来

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

ES6基础知识八:你是怎么理解ES6中Proxy的?使用场景?

一、介绍 定义&#xff1a; 用于定义基本操作的自定义行为 本质&#xff1a; 修改的是程序默认形为&#xff0c;就形同于在编程语言层面上做修改&#xff0c;属于元编程(meta programming) 元编程&#xff08;Metaprogramming&#xff0c;又译超编程&#xff0c;是指某类计算…

ChatGPT和搜索引擎哪个更好用

目录 ChatGPT和搜索引擎的概念 ChatGPT和搜索引擎的作用 ChatGPT的作用 搜索引擎的作用 ChatGPT和搜索引擎哪个更好用 总结 ChatGPT和搜索引擎的概念 ChatGPT是一种基于对话的人工智能技术&#xff0c;而搜索引擎则是一种用于在互联网上查找和检索信息的工具。它们各自具…

kaggle新赛:Bengali.AI 语音识别大赛赛题解析

赛题名称&#xff1a;Bengali.AI Speech Recognition 赛题链接&#xff1a;https://www.kaggle.com/competitions/bengaliai-speech 赛题背景 竞赛主办方 Bengali.AI 致力于加速孟加拉语&#xff08;当地称为孟加拉语&#xff09;的语言技术研究。Bengali.AI 通过社区驱动的…

7p透明屏的制造过程复杂琐屑吗?

7p透明屏是一种新型的显示技术&#xff0c;它可以使屏幕变得透明&#xff0c;让用户可以透过屏幕看到背后的物体。这种技术在科幻电影中经常出现&#xff0c;但现在已经成为现实。 7p透明屏的工作原理是利用液晶显示技术和透明材料。液晶显示技术是一种通过控制液晶分子的排列…

【Qt】安装Qt 5.7.1 MSVC2013 64bit版本的说明

【Qt】安装Qt 5.7.1 MSVC2013 64bit版本的说明 1、背景2、安装Qt 5.7.13、运行Qt Creator 1、背景 刚开始Qt是C库&#xff0c;后来Qt发展就越来越强大了。后来Qt 发展成为一套跨平台C图形用户界面应用程序开发框架。 注意它不但可以开发GUI程序&#xff0c;而且也可用于开发非…

Python怎么将图片转换成base64编码

目录 什么是base64编码 Base64编码的特点 Base64编码的应用 Python怎么将图片转换成base64编码 什么是base64编码 Base64编码是一种将二进制数据转换为ASCII字符的编码方式。它是由MIME&#xff08;Multipurpose Internet Mail Extensions&#xff09;规范定义的&#xff0…

怎么清空回收站?3个方法轻松搞定!

有没有大佬知道该怎么清空回收站呀&#xff1f;想把回收站清空了不知道该如何操作&#xff01;求一个清空方法&#xff01;感谢大家啦&#xff01; 电脑的回收站会为我们保存一些删除的数据&#xff0c;如果我们删除文件后意识到这些文件仍然是有用的&#xff0c;可以利用回收站…

Istio Pilot源码学习(一):Pilot-Discovery启动流程、ConfigController配置规则发现

本文基于Istio 1.18.0版本进行源码学习 1、Pilot-Discovery工作原理 Pilot-Discovery是Istio控制面的核心&#xff0c;负责服务网格中的流量管理以及控制面和数据面之间的配置下发 Pilot-Discovery从注册中心&#xff08;如Kubernetes&#xff09;获取服务信息并汇集&#xff…

直播带货app开发开发流程分析

随着小视频管理体系愈来愈变成人们的生活中的一部分&#xff0c;也随之短视频卖货逐步形成岗位内主流的转现方式&#xff0c;将短视频平台生产制造变成短视频带货体系计划愈来愈多&#xff0c;那样&#xff0c;把小视频管理体系开发设计变成短视频带货体系必须两步&#xff1f;…

谷粒商城篇章5 ---- P173-P192 ---- 检索服务【分布式高级篇二】

目录 1 检索服务 1.1 搭建页面环境 1.1.1 引入依赖 1.1.2 将检索页面放到gulimall-search的src/main/resources/templates/目录下 1.1.3 调整搜索页面 1.1.4 将静态资源放到linux的nginx相关映射目录下/root/docker/nginx/html/static/ search/ 1.1.5 SwitchHosts配置域…

疲劳驾驶检测和识别3:Android实现疲劳驾驶检测和识别(含源码,可实时检测)

疲劳驾驶检测和识别3&#xff1a;Android实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 目录 疲劳驾驶检测和识别3&#xff1a;Android实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 1.疲劳驾驶检测和识别方法 2.人脸检测方法 3.疲劳驾驶检测和识别模型…

《人工智能安全》课程总体结构

1 课程内容 人工智能安全观&#xff1a;人工智能安全问题、安全属性、技术体系等基本问题进行了归纳整理。人工智能安全的主要数据处理方法&#xff0c;即非平衡数据分类、噪声数据处理和小样本学习。人工智能技术赋能网络空间安全攻击与防御&#xff1a;三个典型实例及攻击图…