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

16.udp_socket(二)

一.概念回顾

建议先学上篇博客,再向下学习,上篇博客的链接如下:

https://blog.csdn.net/weixin_60668256/article/details/154700406?fromshare=blogdetail&sharetype=blogdetail&sharerId=154700406&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

二.Dictionary的更改

1.UdpServer.hpp的修改

#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <cstring>
#include <errno.h>
#include <strings.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"using namespace LogModule;const static int gsockfd = -1;
const static std::string gdefaultip = "127.0.0.1";//表示本地主机
const static uint16_t gdefaultport = 8080;using func_t = std::function<std::string(const std::string&)>;class UdpServer
{
public:UdpServer(uint16_t port = gdefaultport,func_t func):_sockfd(gsockfd),_addr(port),_isrunning(false),_func(func){}void InitServer(){// 1.创建套接字_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2.1 bind :: 设置进入内核中int n = ::bind(_sockfd,_addr.NetAddr(),_addr.NetAddrLen());if(n < 0){LOG(LogLevel::FATAL) << "bind: " << strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";}void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//将英文转换成为中文std::string result = _func(inbuffer);::sendto(_sockfd,result.c_str(),result.size(),0,CONV(&peer),sizeof(peer));}}_isrunning = false;}~UdpServer(){if(_sockfd > gsockfd){::close(gsockfd);}}private:int _sockfd;InetAddr _addr;bool _isrunning; //服务器运行状态//业务func_t _func;
};#endif

2.Dict.txt的导入

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

3.Dictionary.hpp框架实现

#pragma once#include <iostream>
#include <string>
#include <unordered_map>const std::string gpath = "./";
const std::string gdictname = "Dict.txt";class Dictionary
{
public:Dictionary(const std::string&path = gpath,const std::string& filename = gdictname):_path(path),_filename(gdictname){}bool LoadDictionary(){}std::string translate(const std::string& word){}~Dictionary(){}
private:std::unordered_map<std::string,std::string> _dictionary;std::string _path;std::string _filename;
};

4.LoadDictionary()的实现

bool LoadDictionary(){std::string file = _path + _filename;std::ifstream in(file.c_str());if(!in.is_open()){LOG(LogLevel::ERROR) << "open file " << file << " error";return false;}std::string line;while(std::getline(in,line)){std::string key;std::string value;if(SplitString(line,&key,&value,gsep)){_dictionary.insert(std::make_pair(key,value));}}in.close();}

5.SplitString()的实现

bool SplitString(std::string& line,std::string* key,std::string* value,const std::string& sep)
{auto pos = line.find(sep);if(pos == std::string::npos){return false;}*key = line.substr(0,pos);*value = line.substr(pos + sep.size());if(key->empty() || value->empty()){return false;}return true;
}

6.Translate()的实现

std::string translate(const std::string& word){auto iter = _dictionary.find(word);if(iter == _dictionary.end()){return "None";}return iter->second;}

7.测试代码 + debug

#include "UdpServer.hpp"
#include "Dictionary.hpp"// ./server_udp localport
int main(int argc,char* argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << " localport" << std::endl;Die(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG();std::shared_ptr<Dictionary> dict_sptr = std::make_shared<Dictionary>();std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(port,[&dict_sptr](const std::string& word){return dict_sptr->translate(word);});svr_uptr->InitServer();svr_uptr->Start();return 0;
}

但是我们发现,输入没有回显

void Print(){for(auto& iter : _dictionary){std::cout << iter.first << " : " << iter.second << std::endl;}}

我们可以加一个Print(),进行加载之后我们对应的单词的打印

void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//将英文转换成为中文inbuffer[n] = 0;std::string result = _func(inbuffer);::sendto(_sockfd,result.c_str(),result.size(),0,CONV(&peer),sizeof(peer));}}_isrunning = false;}

三.简易版聊天室

1.聊天室设计

#pragma once#include <iostream>
#include <string>
#include <list>
#include <memory>
#include "InetAddr.hpp"
#include "Log.hpp"using namespace LogModule;class UserInterface
{
public:virtual ~UserInterface() = 0;virtual void SendTo(const std::string& message) = 0;
};class User : public UserInterface
{
public:User(const InetAddr& id):_id(id){}void SendTo(const std::string& message)override{LOG(LogLevel::DEBUG) << "send message to " << _id.Addr() << " info: " << message;}~User(){}
private:InetAddr _id;
};//对用户消息进行路由
//所有用户先进行管理
class UserManager
{
private:std::list<std::shared_ptr<UserInterface>> _inline_user;
};

这里我们使用的室list,(后续的增删用户操作较多,转发消息都是O(n))

然后由用户管理进行消息转发

2.AddUser()(添加用户)的实现

首先我们对应的用户有了,就不用再进行添加了

class User : public UserInterface
{
public:User(const InetAddr& id):_id(id){}void SendTo(int sockfd,const std::string& message)override{LOG(LogLevel::DEBUG) << "send message to " << _id.Addr() << " info: " << message;int n = ::sendto(sockfd,message.c_str(),message.size(),0,_id.NetAddr(),_id.NetAddrLen());(void)n;}bool operator==(const InetAddr& u)override{return _id == u;}~User(){}
private:InetAddr _id;
};

bool operator==(const InetAddr& addr){return _ip == addr._ip;}
class UserInterface
{
public:virtual ~UserInterface() = default;virtual void SendTo(int sockfd,const std::string& message) = 0;virtual bool operator==(const InetAddr& u) = 0;
};
void AddUser(const InetAddr& id){for(auto& user : _online_user){if(*user == id){return;}}_online_user.push_back(std::make_shared<User>(id));}

3.DelUser()的实现

4.Router()的实现

这就是观测者模式

void Router(int sockfd,const std::string& message){for(auto& user:_online_user){user->SendTo(sockfd,message);}}

5.User的SendTo()实现

void SendTo(int sockfd,const std::string& message)override{LOG(LogLevel::DEBUG) << "send message to " << _id.Addr() << " info: " << message;int n = ::sendto(sockfd,message.c_str(),message.size(),0,_id.NetAddr(),_id.NetAddrLen());(void)n;}

6.UdpServer设置回调函数

a.定义回调函数类型

using adduser_t = std::function<void(const InetAddr& id)>;

b.定义成员变量

c.消息发送时增加对应的用户

void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//1.消息内容 && 2.谁给我发的InetAddr cli(peer);inbuffer[n] = 0;//2.新增用户_adduser(cli);}}_isrunning = false;}

d.ServerMain.cc的回调方法

int main(int argc,char* argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << " localport" << std::endl;Die(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG();std::shared_ptr<UserManager> um = std::make_shared<UserManager>();std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>([&um](InetAddr& id){um->AddUser(id);},port);svr_uptr->InitServer();svr_uptr->Start();return 0;
}

所以当我们对应的用户只要进行发送消息,那么我们就能收到用户add的消息

void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//1.消息内容 && 2.谁给我发的InetAddr cli(peer);inbuffer[n] = 0;//2.新增用户_adduser(cli);std::string clientinfo = cli.Ip() + " : " + std::to_string(cli.Port()) + " # " + inbuffer;LOG(LogLevel::DEBUG) << clientinfo;std::string echo_string = "echo# ";echo_string += inbuffer;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));}}_isrunning = false;}

7.线程池做router(转发)

将我们对应的单例线程池的相关代码全部都拷贝到项目中

8.代码修改

void RegisterService(adduser_t adduser,route_t route){_adduser = adduser;_route = route;}

我们在类内定义一个RegisterService方法,然后初始化服务器之后,我们再对该方法进行传参

std::shared_ptr<UserManager> um = std::make_shared<UserManager>();std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(port);svr_uptr->InitServer();svr_uptr->RegisterService([&um](InetAddr& id){um->AddUser(id);},[&um](int sockfd,const std::string& message){um->Router(sockfd,message);});

9.客户端问题解决

这个问题我们要进行解决,所以我们的客户端也要进行多线程进行访问

void* Recver(void* args)
{while(true){struct sockaddr_in temp;socklen_t len = sizeof(temp);char buffer[1024];int n = ::recvfrom(sockfd,buffer,sizeof(buffer)-1,0,CONV(&temp),&len);if(n > 0){buffer[n] = 0;std::cout << buffer << std::endl;}}return nullptr;
}

http://www.dtcms.com/a/600696.html

相关文章:

  • 如何在不使用iTunes的情况下在电脑上访问iPhone文件
  • python+websockets,报错RuntimeError: no running event loop
  • 自己做网站流程龙口市最新公告
  • 自助建站系统介绍wordpress 百度推广
  • 基于Springboot的汽车推荐系统设计与实现7f7h74np(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
  • DBLoss: Decomposition-based Loss Function for Time Series Forecasting 论文阅读
  • STM32F103学习笔记-16-RCC(第4节)-使用 HSI 配置系统时钟并用 MCO 监控系统时钟
  • Git 中新建学习分支 + 暂存修改 + VSCode 可视化查看改动(超详细教程)
  • Linux高效编程与实战:自动化构建工具“make/Makefile”和第一个系统程序——进度条
  • Docker 相关使用收录
  • 【详细步骤解析】爬虫小练习——爬取豆瓣Top250电影,最后以csv文件保存,附源码
  • Docker-存储
  • wap手机网站模板上饶网站建设3ao cc专业a
  • 【Nginx】Nginx 多协议负载均衡实战:StarRocks 与 MinIO 代理配置全解析
  • 域名注册和网站设计服务如何做贴吧类网站多钱
  • python+uniapp基于微信小程序的垃圾分类信息系统
  • C语言编译器安卓版 | 强大功能助力编程学习与实践
  • STM32使用金属探测传感器自制金属探测仪
  • vmware嵌套安装esxi7.0.3扩容vmfs
  • 使用 BR 备份 TiDB 到 AWS S3 存储
  • 【OpenCV + VS】OpenCV 绘图:绘制矩形、圆形、椭圆形、线条等
  • 易语言反编译工具 - 高效破解易语言程序的利器
  • 11年始终专注营销型网站提供网站建设小程序制作
  • AOSP Android13 Launcher3——TransformParams 类
  • 网站推广问题推广公司网站有哪些方式
  • 成都网站建设公司电话美食网站建设多少钱
  • 【把Linux“聊”明白】进程的概念与状态
  • GIT版本管理工具轻松入门 | TortoiseGit,本地 Git 仓库和 Git 概念,笔记02
  • 什么是美颜sdk?美型功能开发与用户体验优化实战
  • 在 React 项目中使用 Ky 与 TanStack Query 构建现代化数据请求层