一.概念回顾
建议先学上篇博客,再向下学习,上篇博客的链接如下:
二.重定向补充知识
cpp
#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;
}





cpp
#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.创建socket
sockfd = ::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 done
while(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随机bind
int n = ::sendto(sockfd,message.c_str(),message.size(),0,CONV(&server),sizeof(server));
(void)n;
}
return 0;
}



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

这样就能很好的进行区分了
三.代码细节补充
1.对用户管理模块进行加锁
cpp
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.消息发送的用户显示
cpp
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.加入用户提醒
cpp
#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.创建socket
sockfd = ::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 done
while(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.退出消息提醒 及 功能实现

cpp
#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.创建socket
sockfd = ::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 done
while(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;
}

cpp
void RegisterService(adduser_t adduser,route_t route,remove_t remove)
{
_adduser = adduser;
_route = route;
_remove = remove;
}
cpp
#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()了
删除用户实现
cpp
void DelUser(InetAddr& id)
{
//v1
auto 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;
// }
// }
}
cpp
void PrintUser()
{
for(auto user : _online_user)
{
LOG(LogLevel::DEBUG) << "在线用户->" <<user->Id();
}
}
这里将我们所有的列表的在线用户全都打印一遍



5.总结

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

cpp
#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