企业网站的建立多少钱,知识库主题 wordpress,WordPress迁移服务器和域名,二七区室内设计装修公司排名文章目录 实现目标认识相关接口socketbzerobindrecvfromsendto 实现思路和注意事项完整代码Server.hppServer.ccClient.hppClient.cc 运行效果END 实现目标
实现一个服务端和一个客户端#xff0c;客户端负责发送一个单词#xff0c;服务端接收到后将翻译后的结果返回发送到… 文章目录 实现目标认识相关接口socketbzerobindrecvfromsendto 实现思路和注意事项完整代码Server.hppServer.ccClient.hppClient.cc 运行效果END 实现目标
实现一个服务端和一个客户端客户端负责发送一个单词服务端接收到后将翻译后的结果返回发送到客户端。使用UDP网络连接可以跨主机实现通信。服务端读取文件中保存的单词及其翻译通过发送信号使服务端更新词库不需要重启。
认识相关接口
socket
创建套接字文件在Linux一切皆文件。。
#include sys/types.h /* See NOTES */
#include sys/socket.hint socket(int domain, int type, int protocol);参数一为需要选择的通信方式 通常是使用AF_UNIX AF_INET分别表示为本地通信和网络通信。
参数二为套接字提供服务的类型通常使用SOCK_STREAM:流式服务TCP策略SOCK_DGRAM:数据报服务,UDP策略 参数三默认设为0即可因为前面两个参数已经确定好通信的方式和策略
返回值成功创建返回文件的文件描述符 失败返回-1 _sockfd socket(AF_INET, SOCK_DGRAM, 0);
assert(_sockfd ! -1);
cout success : _sockfd endl;bzero
可以将结构体对象初始化和memset同理
#include strings.hvoid bzero(void *s, size_t n);bind
绑定端口号
#include sys/types.h /* See NOTES */
#include sys/socket.hint 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.hssize_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.hssize_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));实现思路和注意事项
思路 首先可以对客户端和服务端分别进行封装两者都具有初始化启动功能。初始化主要负责初始化自身的IP地址端口号和通信方式等两者的启动都必须要有发送和读取的功能客户端先发送再读取服务端先读取再发送服务端要有一个接收到数据后的回调函数对数据进行处理后再发送回去使用C文件操作加载文件里的词库 注意事项 运行服务端时必须带上端口号运行客户端必须带上IP地址和端口号服务端必须显示绑定端口号客户端不需要。操作系统会帮客户端自动生产并绑定端口号因为服务端是只有一个而访问这个服务端的客户端却会有很多个。服务端的IP地址不能够指定某个特定的IP地址必须使用0.0.0.0因为会有很多个客户端访问如果指明一个特定的IP地址那么就可能出现别的IP访问不了端口号注意端口号必须要调用接口去转换一下大小端因为很多情况下都不清楚机器的大小端养成好习惯所有接口的参数都是 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 functionvoid(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_mapstring, 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_ptrudpServer 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_ptrudpClient cs(new udpClient(server_ip, server_port));cs-clientInit();cs-run();return 0;
}运行效果
初始词库 运行效果 更新后词库 运行不需要重启服务端发送2号信号ctrl c END
以上就是本篇简易的UDP英汉词典了期待各位佬们能够指点一二。