当前位置: 首页 > news >正文

哪家小吃培训网站做的最好网站建设数据安全的意义

哪家小吃培训网站做的最好,网站建设数据安全的意义,宁波seo教程app推广,制作网站开发多少钱其实经过这几天写的几种不同的UDP的简易客户端与服务端#xff0c;还是很有套路的#xff0c;起手式都是非常像的。 更多的难点对我来说反而是解耦#xff0c;各种各样的function一用#xff0c;回调函数一调#xff0c;呕吼#xff0c;就会懵一下。 对于这篇文章#x…其实经过这几天写的几种不同的UDP的简易客户端与服务端还是很有套路的起手式都是非常像的。 更多的难点对我来说反而是解耦各种各样的function一用回调函数一调呕吼就会懵一下。 对于这篇文章我主要是把那些起手式还有我觉得有点难得解耦方式稍微进行一下说明方便我自己回顾当然如果可以帮助更多的小伙伴那自然是更好啦。 目录 Echo服务端起手式服务端LOOP客户端起手客户端LOOP验证 Dict设计思想验证 Chat服务端的修改客户段的修改效果展示 Echo 服务端起手式 这个echo的代码是为了熟悉起手式因为几乎没有业务的附带所以是很简单的。 而它的功能就是你向服务器中发送消息你的服务端会重新发给你。 注意日志真的很重要可以让你知道你的程序在哪一步出错了很快的定位。 首先大概的看一下起手式接口 因为我们的网络要通信需要IP 端口号才能定位到具体主机内具体进程而IP port就是我们说的套接字关于套接字在UDP中需要知道2个接口但是这两个接口中我们注意到有一个sockaddr 结构体因此我们需要看一下结构体。 // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol); // 绑定端口号 (TCP/UDP, 服务器) int bind(int socket, const struct sockaddr *address, socklen_t address_len);首先这组接口不仅可以实现网络通信也可以实现主机内通信。 其中sockaddr_in是网络sockaddr_un是主机内那么为什么bind接口内是sockaddr 因为我们会填16位地址类型所以当我们将对应的结构体强转为sockaddr *时函数内部就会根据16位地址类型判断究竟是哪一种这也就是C语言层面的多态! 首先我们的server是面向对象的代码中没有定义就出现变量都是私有成员。根据名字都可以知道大概意思在最后会有完整代码。 int fd ::socket(AF_INET, SOCK_DGRAM, 0);if (fd 0){exit(1);}_socketfd fd;// 将Ip Port与套接字绑定struct sockaddr_in addr;memset(addr, 0, sizeof(addr));addr.sin_family AF_INET;// 不要忘记转为网络序列// addr.sin_addr.s_addr inet_addr(_ip.c_str());addr.sin_addr.s_addr INADDR_ANY;addr.sin_port htons(_port);// bindint n ::bind(_socketfd, (struct sockaddr *)addr, sizeof(addr));再来解释一下代码 第一个参数 因为我们是网络通信所以16位网络地址选择使用AF_INET 第二个参数 我们选择的是UDP 也就是无连接不可靠数据报。 第三个参数 表示希望使用的协议我们通常设置为0系统会根据情况自己处理。 返回值 socket返回的是一个文件描述符 为什么返回文件描述符 我们在此感性的理解 因为我们网络的通信是建立在网卡上的而linux中一切皆文件所以就相当于我们返回的是网卡的文件描述符。 于是我们的socket就创建好了但是还要与IP与port进行绑定起来。 那么就先要创建一个sockaddr_in的结构体填参 sin我们可以理解为socket Internet。 其中我们只关心图中的框起来部分sin_zero是作为将结构体补齐用的新的网络编程库中甚至都见不到这个字段了。 我们逐个分析一下这个要填的3个字段 第一个参数的形式是一个宏在预处理阶段会进行处理进行替换得到sa_family_t sin_family##在预处理阶段会将两边的字符串进行拼接。 而这个填的就是AF_INET与套接字对应。 第二个参数是一个无符号短整型uint16_t的类型我们填入自定义端口即可。 注意由于我们要注意网络序列与主机序列的转换自己进行判断的话过于麻烦OS也提供了一组接口方便我们进行转换。 第三个参数是IP 注意这个IP可是有很大讲究的 首先他是结构体内嵌套结构体填的时候要注意 其次我们刚开始肯定觉得绑定自己的公网IP或者局域网IP又或者是本地环回。 但是如果填一个具体的IP那么就意味着你以后只能从这一个向指定的IP中获取信息但是你的主机IP有多个反而不能全部利用因此这里我们选择填入INADDR_ANY0。 此时我们就可以接收多个IP端口号发送来的信息了。 注意我们一般在进行网络测试时一般会使用本地环回IP测试也就是127.0.0.1当你的客服端向127.0.0.1这个IP发送时那么就不会在网络中传输而是在本机。 那么此时我们就完成起手式socket的创建与绑定了。 服务端LOOP while (true) {char buffer[1024];sockaddr_in peer;socklen_t len sizeof(peer);int n recvfrom(_socketfd, buffer, sizeof(buffer) - 1, 0, (sockaddr*)peer, len);if (n ! -1){buffer[n] 0;int m sendto(_socketfd, buffer, n, 0, (struct sockaddr*)peer, sizeof(peer));if (m -1){perror(发送错误);break;}} }我们的服务端肯定是要进行接收消息的然后在做一些加工返回给客服端。 这里就不得不说两个函数了。 他们的参数都非常的类似在接收时我们传入一段缓冲区填入大小即可得到客户端发来的消息了因为我们接收后还要发送给对方所以后边的两个参数是输入型参数会得到对方的sockaddr信息。 对于发送时我们也是如此。 另外它们的 flags 参数是用来控制函数行为的标志位允许程序员指定一些特殊的选项或操作模式。flags 参数通常是多个标志的按位或OR组合但大多数情况下这些标志并不是必需的因此绝大多数会传递0作为默认值。 客户端起手 与服务端有很大的不同。 int fd socket(AF_INET, SOCK_DGRAM, 0); if (fd -1) {exit(1); }先说结论我们在服务端只需要创建socket即可肯定需要bind但无需显示bind因为会在sendto中OS会自主绑定。 为什么不需要自己指定IP 端口号 OS肯定是知道你的IP那么OS给你绑定也说得过去那么端口号为什么不自主绑定 我们举一个例子一个主机上的端口号是有限的如果客户端是自主定义那么可能不同客户端会出现重复比如抖音用端口号8888那么快手绑定8888是势必不成功因为IP port标识一个唯一进程。所以这个由OS自主分配即可。 客户端LOOP while (true) {std::string buffer;std::cout Please write msg:;std::getline(std::cin, buffer);// 处理sockaddr结构体 发送数据到服务端struct sockaddr_in peer;peer.sin_addr.s_addr inet_addr(ip.c_str());peer.sin_port htons(port);peer.sin_family AF_INET;socklen_t len sizeof(peer);int n sendto(fd, buffer.c_str(), buffer.size(), 0, (struct sockaddr *)peer, len);if (n ! -1){char inbuffer[1024];struct sockaddr_in temp;socklen_t len;int m recvfrom(fd, inbuffer, n, 0, (struct sockaddr *)temp, len);if (m ! -1){inbuffer[m] 0;std::cout inbuffer std::endl;}} }我们在服务端绑定时不需要填真正的IP发送和接收时也直接使用现成的但是在客户端我们需要手动填写但是我们现在有的是一个字符串我们压迫将他转为4字节还要转为网络序列这也是很繁琐的因此也有一批函数用来转化。 可以看到我们放入一个字符串地址即可得到网络序列4字节IP非常的方便但是这里可以改进我们最后说一般使用inet_pton。 所以我们现在也就没啥干货了起手式已经完成 验证 由于上图代码都是非常简略的并没有将如何封装写出但是还是要验证一下的 完整代码在Gitee链接中给出。 Dict 设计思想 我们在以上的基础上增加一些业务这里也就开始涉及一些解耦的设计了。 我们的理想效果为输入一个单词返回他的意思。 其实服务器与客户端大的逻辑仍旧是不变的但是这里进行设计解耦的思想是很好的要学习! 首先我们要改变的就是服务端我们在recvfrom到字符串单词后可以使用回调函数将这个单词交给外部来做返回汉语意思字符串sendto。这样就很好的完成了解耦因为我们要用到回调函数所以也就意味着在构造服务端对象时要传入可调用对象。 此时我们就可以利用function进行包装包装出一个可调用对象类型。 using func_t std::functionstd::string(std::string); 关于这里我其实还想补充几点 我们命名时可以看到func_t中的func代表这是函数t代表typename表示类型这样别人一看就知道这是一个函数类型。 而命名空间时也有这样的讲究比如我们有一个日志类使用log_ns域封装起来ns就是namespace的缩写也是一目了然。 另外就是关于function的一些点了实际上我们的function绑定时与被绑定的函数类型并不需要完全相符就像下图这样的代码甚至可以编过我一点都不理解… 但是我认为还是最好保证一样。 回到主线 由于我们希望解耦因此function中我们也就没有必要传引用就解耦解的结结实实但实际上传也是可以的还避免了拷贝。 随后我们再编写一个字典类最终要的是要有支持翻译的功能 我们可以选择搞一个配置文件的形式创建对象时进行加载即可~ const std::string sep : ;// 单词与翻译的分隔符class Dict { private:void Load(){std::ifstream in(_path.c_str());if (!in.is_open()){exit(1);}std::string line;while (std::getline(in, line)){std::string key;std::string value;auto pos line.find(sep);if (pos -1)continue;key line.substr(0, pos);value line.substr(pos sep.size());_map.insert(std::make_pair(key, value));}}public:Dict(const std::string path): _path(path){Load();}std::string GetChinese(std::string word){auto ite _map.find(word); if (ite ! _map.end())return ite-second;elsereturn None;}~Dict(){} private:std::unordered_mapstd::string, std::string _map; std::string _path; };其中GetChinese函数就是我们未来在服务端回调的函数 但是此时要注意我们不能直接将这个函数传入服务器的构造函数中因为这是一个静态成员函数所以我们需要将这个函数bind一下让this指针隐式写入即可 我觉得这就是最精髓的地方了。 代码见链接。 验证 Chat 这是一个聊天室项目我觉得是还算挑战性的但实际上只是套的层数有点的好多整合在一起。 听说Java那更喜欢各种封,各种套什么结构啥的害怕~ 但是对于当前的chat聊天室来说最重要的搞清楚整体的大框架。 不仅仅是对于当前的聊天室甚至可以说是任何比较嵌套的只要把结构搞清楚了那么就会轻松很多。 我们现在直接使用以上的服务端 客户端进行改进即可。 线程池的代码 服务端的修改 我们在字典中已经学到了在服务端使用回调进行业务处理我们当然也可以使用回调完成转发 我们进行转发需要3个元素。 sockfd描述符 message消息体sockaddr结构体。 因此我们的回调设置为这样子即可。 这里的Inet就是我们带码云中封装过的转化类 using service_t std::functionvoid(int, const std::string , const Inet );在服务器端上一个版本原本调用处理获取翻译的地方更改一下即可~ 所以又到了设计转发类的时候了我们一般喜欢在应用层中把这个工作叫做路由。 也就是设计一个路由类。 我们这样设计当服务端回调到路由模块时我们就得到了sockfdmessageaddr 首先检查当前ip port是否在在线列表中不在就add在了就不管。当消息为QUIT或者Q时将在线列表中的user删除转发我们只需要遍历一遍在线用户列表即可 也就是转发时使用线程池。 class Route { public:Route(){}void CheckOnlineUsers(const Inet inet_addr){_online_users.insert(inet_addr)}void Offline(const Inet inet_addr){LOG(DEBUG, %s offline\n, inet_addr.AddrStr().c_str());_online_users.erase(inet_addr);}void ForwardHelper(int socket, const std::string message){for (auto user : _online_users){sockaddr_in peer user.Sockaddr();socklen_t len sizeof(peer);int n ::sendto(socket, message.c_str(), message.size(), 0, (struct sockaddr *)peer, len);}}void Forward(int socket, const std::string message, const Inet inet_addr){CheckOnlineUsers(inet_addr);if (message Q || message QUIT){Offline(inet_addr);}// 转发模块,线程池去执行std::functionvoid() f std::bind(Route::ForwardHelper, this, socket, send_message);ThreadPoolstd::functionvoid()::GetInstance()-Equeue(f);}~Route(){}private:std::setInet, Route_ns::comp _online_users;pthread_mutex_t _mutex; };注意到我们的线程池中只需要push进去一个可调用对象即可所以我们进行bind一下以进行适配线程池模板。 而我们在进行构造客户端时传入Route类中的Forward函数即可~ 依旧和Dict服务器一样的方法套路。 这样服务端就设计好了 客户段的修改 我们当前的客户端首先是有问题的因为我们当前只有一个线程同时进行收和发当我们多起几个客户端时如果客户端A进行发消息其他的客户端其实都不会显示的因为只有别的客户端进行sendto时才会收到消息否则就一直阻塞在sendto中。 所以这里我们也是用多线程进行一下修改一个线程一直读一个一直进行发送。 main函数中我们创建2个线程分别执行各自的读和写这里就没什么细节了。 int ClientInit() {int fd socket(AF_INET, SOCK_DGRAM, 0);if (fd -1){LOG(FATAL, create socket err);exit(1);}return fd; }void receiver(const std::string name, int socketfd) {while (true){char inbuffer[1024];struct sockaddr_in temp;socklen_t len;int n recvfrom(socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)temp, len);if (n 0){inbuffer[n] 0;std::cerr inbuffer std::endl;}} }void sender(const std::string name, int socketfd, const std::string ip, uint16_t port) {while (true){std::string buffer;std::cout Please input msg:;std::getline(std::cin, buffer);// 处理sockaddr结构体 发送数据到服务端struct sockaddr_in peer;peer.sin_addr.s_addr inet_addr(ip.c_str());peer.sin_port htons(port);peer.sin_family AF_INET;socklen_t len sizeof(peer);int n sendto(socketfd, buffer.c_str(), buffer.size(), 0, (struct sockaddr *)peer, len);} }int main(int args, char *argv[]) {// 处理命令行行参数if (args ! 3){std::cerr Usage: argv[0] Ip Port std::endl;exit(1);}std::string ip argv[1];uint16_t port std::stoi(argv[2]);int socketfd ClientInit();MyThread t1(thread-receiver, std::bind(receiver, std::placeholders::_1, socketfd));MyThread t2(thread-sender, std::bind(sender, std::placeholders::_1, socketfd, ip, port));t1.Start();t2.Start();t1.Join();t2.Join();return 0; }代码链接 效果展示 完~~
http://www.hkea.cn/news/14419156/

相关文章:

  • 企业网站建设 新天地网络wordpress做多重筛选
  • 《基层建设》在哪个网站收录的什么是公司主页
  • 网站网页基本情况 网页栏目设置外军网站建设
  • 十大中国网站制作wordpress 面板
  • 建设部网站39文件西安装修公司
  • 做网站哪个行业比较有前景滨州市住房和城乡建设部网站
  • 网站做小学一年二班作业怎么做高端专区
  • 一键网站模块wordpress全站采集
  • 免费服务器永久郑州官网优化推广
  • 深圳网站优化企业太原网站建设方案
  • 专业优化网站建设安徽工程建设信息网
  • 建公司网站哪家公司好做的网站怎样更新
  • 漳平网络建站公司短租网站开发
  • 网站后台插件下载seo泛站群
  • 做网站需要什么学历西安跨境电子商务平台网站
  • 广东网站建设微信商城运营nginx php7 wordpress
  • 诚信经营网站的建设爱站网影视排行榜
  • 南宁保洁网站建设好人有好报
  • 网站建设所需硬件参数第三方网站开发的商家
  • 大型网站建设报价英文学习网站
  • 广州模板建站平台医院诊所响应式网站模板
  • 怎么免费永久创建网站无广告自己做的网站怎么改电话
  • 旅行社网站模版网站建设推广好做吗
  • 沈阳企业网站制作公司大良网站建设机构
  • 深圳做公司网站公司建设网站费用怎么记账
  • 海淀周边网站建设网站建设一般报价多少
  • 潍坊网站网站建设wordpress页面加载动画插件
  • 公司简介网站怎么做个人如何网站备案
  • 沈阳网站制作系统网校网站建设多少钱
  • 了解深圳网站定制开发wordpress后台变慢