16.udp_socket(三)
一.概念回顾
建议先学上篇博客,再向下学习,上篇博客的链接如下:
https://blog.csdn.net/weixin_60668256/article/details/154725707?fromshare=blogdetail&sharetype=blogdetail&sharerId=154725707&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link
二.重定向补充知识
#include <cstdio>
#include <iostream>int main()
{//标志输出std::cout << "hello cout" << std::endl;printf("hello printf\n");//标志错误std::cerr << "hello cerr" << std::endl;fprintf(stderr,"hello fprintf\n");return 0;
}





#include "UdpClient.hpp"
#include <iostream>
#include <cstring>
#include <string.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Common.hpp"
#include <pthread.h>int sockfd = -1;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::cerr << buffer << std::endl;}}return nullptr;
}// ./client_udp serverip serverport
int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "Usage: " << argv[0] << " serverip serverport" << std::endl;Die(USAGE_ERR);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//1.创建socketsockfd = ::socket(AF_INET,SOCK_DGRAM,0);if (sockfd < 0){std::cerr << "scoket error" << std::endl;Die(SOCKET_ERR);}//1.1填充server信息struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_port = ::htons(serverport); //要被发送给对方的,即要发送到网络中server.sin_addr.s_addr = ::inet_addr(serverip.c_str());pthread_t tid;pthread_create(&tid,nullptr,Recver,nullptr);//2.client donewhile(true){std::cout << "please Enter# " << std::endl;std::string message;std::getline(std::cin,message);// client 不需要进行bind吗? socket <->socket// client 必须要有自己的ip和端口!但是客户端,不需要自己显示调用bind!!// 而是,客户端首次sendto消息的时候,由OS自动进行bind// 1.如何理解client自动随机bind端口号? 一个端口号,由OS自动进行bind(客户端不需要被人找到)// 2.如何理解server需要显示的bind? 服务器端口号必须稳定,所以这里不能由OS随机bindint n = ::sendto(sockfd,message.c_str(),message.size(),0,CONV(&server),sizeof(server));(void)n;}return 0;
}



服务器转发的是2号信号,所以会在上面汇总

这样就能很好的进行区分了
三.代码细节补充
1.对用户管理模块进行加锁
class UserManager
{
public:UserManager(){}void AddUser(InetAddr& id){LockGuard lockguard(_mutex);for(auto& user : _online_user){if(*user == id){return;}}LOG(LogLevel::INFO) << "新增该用户: " << id.Addr();_online_user.push_back(std::make_shared<User>(id));}void DelUser(const InetAddr& user){}void Router(int sockfd,const std::string& message){LockGuard lockguard(_mutex);for(auto& user:_online_user){user->SendTo(sockfd,message);}}~UserManager(){}
private:std::list<std::shared_ptr<UserInterface>> _online_user;Mutex _mutex;
};

2.消息发送的用户显示
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;std::string message = cli.Addr() + "# " + inbuffer;//2.新增用户_adduser(cli);//3.新增转发模块task_t task = std::bind(UdpServer::_route,_sockfd,message);ThreadPool<task_t>::getInstance()->Equeue(task);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;}



这样我们再启动的时候,绑定的端口就能识别到
3.加入用户提醒
#include "UdpClient.hpp"
#include <iostream>
#include <cstring>
#include <string.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Common.hpp"
#include <pthread.h>int sockfd = -1;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::cerr << buffer << std::endl;}}return nullptr;
}// ./client_udp serverip serverport
int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "Usage: " << argv[0] << " serverip serverport" << std::endl;Die(USAGE_ERR);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//1.创建socketsockfd = ::socket(AF_INET,SOCK_DGRAM,0);if (sockfd < 0){std::cerr << "scoket error" << std::endl;Die(SOCKET_ERR);}//1.1填充server信息struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_port = ::htons(serverport); //要被发送给对方的,即要发送到网络中server.sin_addr.s_addr = ::inet_addr(serverip.c_str());pthread_t tid;pthread_create(&tid,nullptr,Recver,nullptr);//1.2启动的时候,给服务器推送消息即可const std::string online = "...来了哈!";int n = ::sendto(sockfd,online.c_str(),online.size(),0,CONV(&server),sizeof(server));//2.client donewhile(true){std::cout << "please Enter# " << std::endl;std::string message;std::getline(std::cin,message);int n = ::sendto(sockfd,message.c_str(),message.size(),0,CONV(&server),sizeof(server));(void)n;}return 0;
}

4.退出消息提醒 及 功能实现

#include "UdpClient.hpp"
#include <iostream>
#include <cstring>
#include <string.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Common.hpp"
#include <pthread.h>
#include <signal.h>int sockfd = -1;
struct sockaddr_in server;void ClientQuit(int signo)
{(void)signo;const std::string quit = "Quit!";int n = ::sendto(sockfd,quit.c_str(),quit.size(),0,CONV(&server),sizeof(server));
}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::cerr << buffer << std::endl;}}return nullptr;
}// ./client_udp serverip serverport
int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "Usage: " << argv[0] << " serverip serverport" << std::endl;Die(USAGE_ERR);}signal(2,ClientQuit);std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//1.创建socketsockfd = ::socket(AF_INET,SOCK_DGRAM,0);if (sockfd < 0){std::cerr << "scoket error" << std::endl;Die(SOCKET_ERR);}//1.1填充server信息memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_port = ::htons(serverport); //要被发送给对方的,即要发送到网络中server.sin_addr.s_addr = ::inet_addr(serverip.c_str());pthread_t tid;pthread_create(&tid,nullptr,Recver,nullptr);//1.2启动的时候,给服务器推送消息即可const std::string online = "...来了哈!";int n = ::sendto(sockfd,online.c_str(),online.size(),0,CONV(&server),sizeof(server));//2.client donewhile(true){std::cout << "please Enter# " << std::endl;std::string message;std::getline(std::cin,message);int n = ::sendto(sockfd,message.c_str(),message.size(),0,CONV(&server),sizeof(server));(void)n;}return 0;
}

void RegisterService(adduser_t adduser,route_t route,remove_t remove){_adduser = adduser;_route = route;_remove = remove;}
#include "UdpServer.hpp"
#include "User.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<UserManager> um = std::make_shared<UserManager>();std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(port);svr_uptr->RegisterService([&um](InetAddr& id){um->AddUser(id);},[&um](int sockfd,const std::string& message){um->Router(sockfd,message);},[&um](InetAddr& id){um->DelUser(id);});svr_uptr->InitServer();svr_uptr->Start();return 0;
}

所以,后续如果我们执行对应的移除方法,那我们使用的就必定是对应的um->DelUser()了
删除用户实现
void DelUser(InetAddr& id){//v1auto pos = std::remove_if(_online_user.begin(),_online_user.end(),[&id](std::shared_ptr<UserInterface>& user){return *user == id;});_online_user.erase(pos,_online_user.end());//v2// for(auto user : _online_user)// {// if(*user == id)// {// _online_user.erase(user);// break;// }// }}
void PrintUser(){for(auto user : _online_user){LOG(LogLevel::DEBUG) << "在线用户->" <<user->Id();}}
这里将我们所有的列表的在线用户全都打印一遍



5.总结

四.服务器防止拷贝
#pragma once
#include <iostream>class nocopy
{
public:nocopy(){}nocopy(const nocopy &) = delete;const nocopy& operator = (const nocopy &) = delete;~nocopy(){}
};
当我们的服务器不想被拷贝,我们可以进行继承上面的nocopy的类即可

#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#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 <functional>#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"using namespace LogModule;
using namespace ThreadPoolModule;const static int gsockfd = -1;
const static std::string gdefaultip = "127.0.0.1";//表示本地主机
const static uint16_t gdefaultport = 8080;using adduser_t = std::function<void(InetAddr& id)>;
using task_t = std::function<void()>;
using route_t = std::function<void (int sockfd,const std::string& message)>;
using remove_t = std::function<void(InetAddr& id)>;class nocopy
{
public:nocopy(){}nocopy(const nocopy &) = delete;const nocopy& operator = (const nocopy &) = delete;~nocopy(){}
};class UdpServer : public nocopy
{
public:UdpServer(uint16_t port = gdefaultport):_sockfd(gsockfd),_addr(port),_isrunning(false){}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 RegisterService(adduser_t adduser,route_t route,remove_t remove){_adduser = adduser;_route = route;_remove = remove;}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;std::string message;if(strcmp(inbuffer,"Quit!") == 0){//移除观测者_remove(cli);message = cli.Addr() + "# " + "我走了,你们聊!";}else{//2.新增用户_adduser(cli);message = cli.Addr() + "# " + inbuffer;}//3.新增转发模块task_t task = std::bind(UdpServer::_route,_sockfd,message);ThreadPool<task_t>::getInstance()->Equeue(task);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;}~UdpServer(){if(_sockfd > gsockfd){::close(gsockfd);}}private:int _sockfd;InetAddr _addr;bool _isrunning; //服务器运行状态//新增用户adduser_t _adduser;//移除用户remove_t _remove;//数据转发route_t _route;
};#endif