对商家而言网站建设的好处同心食品厂网站建设项目任务分解
web/
2025/9/30 10:00:22/
文章来源:
对商家而言网站建设的好处,同心食品厂网站建设项目任务分解,网站开发两端对齐底行左对齐,西安企业免费建站目录
编辑
一#xff0c;引入
二#xff0c;在Server端修改的代码 1#xff0c;保存用户信息功能实现
2#xff0c;拼接消息 3#xff0c;广播消息
三#xff0c; Client端要修改的代码 四#xff0c;效果演示 一#xff0c;引入 在上一篇文章udp网络服务器中引入
二在Server端修改的代码 1保存用户信息功能实现
2拼接消息 3广播消息
三 Client端要修改的代码 四效果演示 一引入 在上一篇文章udp网络服务器中我实现了一个简易版的网络服务器。而在今天的这篇文章中我要实现的便是基于这个udp网络服务器而实现的多人聊天室。这个聊天室的新增功能如下 1添加用户。 2广播消息。 3使用不同的线程来接收消息和和发送消息。 二在Server端修改的代码 1保存用户信息功能实现
这个功能的实现其实就像我们日常生活中的通讯录的作用一样。我们可以用线性表等方式实现。但是为了提高查找效率我采用的方式是使用哈希表的方式实现。 std::unordered_mapint,sockaddr_in online_user_;//建立一个用户上线表用哈希表的方式存储
哈希表参数类型解释 int:因为要存放的是接收方的ip地址。 sockaddr_in:套接字类型可以通过这个参数来获取接收方的端口号。 有了表以后就要来想想一个常识性问题了。我们的表里面需要有重复信息吗答案当然是不需要。所以在将数据插入到表里时我们要检查这个表里面的数据是否和当前要插入的数据重复,暂时通过ip的方式识别。
bool Check_user(int clientId)//检查是否为新用户{if(online_user_.find(clientId) online_user_.end()){return true;}return false;} 为什么是在Run函数里面修改 因为在这个函数内部实现了接收消息的功能所以发送方的端口号和ip等消息便可以在这个函数内获得。 2拼接消息
在得到发送方的端口号和ip以后为了标识显示发送方。那我们便要将发送方的ip和port以及发送的消息拼接在一起
//拼接消息
inbuf[r1] 0;
std::string ip inet_ntoa(si.sin_addr);
std::string port std::to_string(si.sin_port);
std::string message inbuf;
std::string tostring [ip:port] message; 3广播消息
在做完用户表的添加和消息的拼接以后我们便知道了消息是什么消息要发给谁。所以我们便可以开始广播消息让所有人看到消息了。
void Broad_cast(std::string message)//广播函数{for(auto e:online_user_)//广播{socklen_t len sizeof(e.second);int r sendto(socketfd_, message.c_str(), message.size(), 0, (sockaddr*)e.second, len);if(r0){std::cout broad cast error! std::endl;continue;}}} 采用循环的方式将消息发送给用户表里的所有人。 所以我们在run函数里面要修改的全部代码便是 void Run(const funcfun)//加入远程操作{char inbuf[inbufSize] {0};sockaddr_in si;socklen_t len sizeof(si);//一定要初始化while (true){int r1 recvfrom(socketfd_, inbuf, sizeof inbuf-1, 0, (sockaddr *)si, len);//收消息if(r10)//读取消息失败{perror(recvfrom error);exit(10);}if (Check_user(si.sin_addr.s_addr)) // 检查是否是新用户{std::cout welcome....... std::endl;online_user_.insert({si.sin_addr.s_addr, si});//加入到用户表里}//将消息发送给用户//拼接消息inbuf[r1] 0;std::string ip inet_ntoa(si.sin_addr);std::string port std::to_string(si.sin_port);std::string message inbuf;std::string tostring [ip:port] message;//将消息广播Broad_cast(tostring);//std::cout tostring std::endl;// std::cout 收到消息正在处理std::endl;// std::string command inbuf;// std::string cip inet_ntoa(si.sin_addr);// int cport si.sin_port;// std::string message fun(command,cip,cport );// // std::cout message std::endl;// int r2 sendto(socketfd_, message.c_str(), message.size(), 0, (sockaddr *)si, sizeof si);//将处理结果返回给发送方// std::cout std::endl处理完成;// if (r2 0)// {// perror(server send message error);// continue;// }}} 对于server端的代码我们只需要修改Run函数里面的代码即可。 三 Client端要修改的代码
在平时的生活中我们很容易的便可以知道。收发消息是可以同时的运行的。所以在实现Client端的代码时我们最好创建两个线程实现收发消息的并发执行。
void Run(){// 客户端接收函数分两个线程执行// 定义线程变量pthread_t Send_thread;pthread_t Receve_thread;pthread_create(Send_thread, nullptr, Send, this);pthread_create(Receve_thread, nullptr, Receve, this);pthread_join(Send_thread, nullptr);pthread_join(Receve_thread, nullptr);// char outbuf[outbufSize];// while (true)// {// std::cout 请输入内容 ;// std::getline(std::cin, requestes); // client输入内容// if (sendto(socketfd_, requestes.c_str(), sizeof requestes, 0, (sockaddr *)si, sizeof si) 0) // 发送消息// {// continue;// }// int r3 recvfrom(socketfd_, outbuf, sizeof outbuf, 0, (sockaddr *)si, len);// if(r30)// {// continue;// }// outbuf[r3] 0;// std::cout outbuf std::endl;// memset(outbuf, 0, sizeof outbuf);// }}
在这段代码中我将Client端的Run函数修改如上。Run函数的作用便只是创建两个线程然后再执行对应的方法。这两个方法便是接收消息和发消息。
接收消息 static void *Receve(void *args) // 收消息的线程{Client *C static_castClient *(args);char outbuf[outbufSize];sockaddr_in si;socklen_t len sizeof(si);C-Dup(); // 重定向到别的窗口while (true){int r3 recvfrom(C-socketfd_, outbuf, sizeof outbuf-1, 0, (sockaddr *)si, len);if (r3 0){continue;}outbuf[r3] 0;std::cerr outbuf std::endl;memset(outbuf, 0, sizeof outbuf);}} 发消息
static void *Send(void *args) // 发消息的线程{Client *C static_castClient *(args);std::string requestes;sockaddr_in si;socklen_t len;bzero(si, sizeof si);si.sin_family AF_INET;si.sin_port htons(C-port_);si.sin_addr.s_addr inet_addr(C-ip_.c_str());while (true){std::cout 请输入内容 ; // client输入内容std::string requestes;std::getline(std::cin, requestes);if (sendto(C-socketfd_, requestes.c_str(), requestes.size(), 0, (sockaddr *)si, sizeof si) 0) // 发送消息{continue;}}}
解释 1为什么要使用static函数来修饰这两个方法 因为pthread_create函数里面的方法类型是void*(void*)但是类里面的成员方法的参数里面有一个隐藏的this指针。所以只能使用static修饰让成员方法变成静态成员方法进而去掉前面隐藏的this指针。 2pthread_create函数中为什么要传入this指针 因为在这两个函数的内部要使用类的私有成员。但是没有this指针不能直接调用。所以便传入this指针来进行调用私有成员。 3为什么*Receve方法中的Dup()函数是什么 其实这是一个重定向的函数。主要是为了实现输入和输出的分离。让输入和输出打印在不同的终端。 代码实现如下 void Dup()
{int fd open(/dev/pts/17, O_WRONLY|O_CREAT);//这是一个终端文件路径//可以使用w命令查看自己打开的终端号if(fd0) //终端号是数字前面部分的路径是一样的{perror(open error);exit(30);}dup2(fd, 2);//重定向重定向的是2号标准错误。因为我的消息是用cerr输出的。
} 补充 如果不想实现重定向的功能也可以在启动可执行程序时直接重定向到不同的终端 首先要使用w来查看自己的终端号是什么 然后使用重定向操作 ./Server 8080 /dev/pts/17 ./Client 111.230.60.61 8080 /dev/pts/18 四效果演示
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/84397.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!