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

【Linux | 网络】socket编程 - 使用UDP实现服务端向客户端提供简单的服务

在这里插入图片描述

目录

  • 一、UdpServerSever(客户端发送信息,服务端直接返回信息)
    • 1.1 Comm.hpp(公共数据)
    • 1.2 Log.hpp(日志)
    • 1.3 InetAddr.hpp(管理sockaddr_in相关信息)
    • 1.4 NoCopy.hpp(防拷贝)
    • 1.5 UdpServer.hpp(服务端封装)
    • 1.6 Main.cpp(服务端)
    • 1.7 UdpClient.cpp(客户端)
  • 二、UdpServerExecute(客户端发送指令服务端执行后返回执行结果)
    • 2.1 Comm.hpp(公共数据)
    • 2.2 Log.hpp(日志)
    • 2.3 InetAddr.hpp(管理sockaddr_in相关信息)
    • 2.4 NoCopy.hpp(防拷贝)
    • 2.5 UdpServer.hpp(服务端封装)
    • 2.6 Main.cpp(服务端)
    • 2.7 UdpClient.cpp(客户端)
  • 三、UdpServerChat(实现简单的聊天室)
    • 3.1 Comm.hpp(公共数据)
    • 3.2 Log.hpp(日志)
    • 3.3 InetAddr.hpp(管理sockaddr_in相关信息)
    • 3.4 NoCopy.hpp(防拷贝)
    • 3.5 Lockguard.hpp(自动管理锁)
    • 3.6 Thread.hpp(封装线程)
    • 3.7 ThreadPool.hpp(线程池)
    • 3.8 Daemon.hpp(使进程变为守护进程)
    • 3.9 UdpServer.hpp(服务端封装)
    • 3.10 Main.cpp(服务端)
    • 3.11 UdpClient.cpp(客户端)
  • 结尾

一、UdpServerSever(客户端发送信息,服务端直接返回信息)

1.1 Comm.hpp(公共数据)

#pragma onceenum {Socket_err = 1,Bind_err,Accept_err,Recvfrom_err
};

1.2 Log.hpp(日志)

#pragma once#include <pthread.h>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>using namespace std;// 日志等级
enum
{Debug = 0, // 调试Info,      // 正常Warning,   // 警告Error,     // 错误,但程序并未直接退出Fatal      // 程序直接挂掉
};enum
{Screen = 10, // 打印到显示器上OneFile,     // 打印到一个文件中ClassFile    // 按照日志等级打印到不同的文件中
};string LevelToString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Unknow";}
}const char* default_filename = "log.";
const int default_style = Screen;
const char* defaultdir = "log";class Log
{
public:Log(): style(default_style), filename(default_filename){// mkdir(defaultdir,0775);pthread_mutex_init(&_log_mutex, nullptr);}void SwitchStyle(int sty){style = sty;}void WriteLogToOneFile(const string& logname, const string& logmessage){int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd == -1)return;pthread_mutex_lock(&_log_mutex);write(fd, logmessage.c_str(), logmessage.size());pthread_mutex_unlock(&_log_mutex);close(fd);}void WriteLogToClassFile(const string& levelstr, const string& logmessage){mkdir(defaultdir, 0775);string name = defaultdir;name += "/";name += filename;name += levelstr;WriteLogToOneFile(name, logmessage);}void WriteLog(int level, const string& logmessage){switch (style){case Screen:{pthread_mutex_lock(&_log_mutex);cout << logmessage;pthread_mutex_unlock(&_log_mutex);}break;case OneFile:WriteLogToClassFile("All", logmessage);break;case ClassFile:WriteLogToClassFile(LevelToString(level), logmessage);break;default:break;}}string GetTime(){time_t CurrentTime = time(nullptr);struct tm* curtime = localtime(&CurrentTime);char time[128];// localtime 的年是从1900开始的,所以要加1900, 月是从0开始的所以加1snprintf(time, sizeof(time), "%d-%d-%d %d:%d:%d",curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday,curtime->tm_hour, curtime->tm_min, curtime->tm_sec);return time;return "";}void LogMessage(int level, const char* format, ...){char left[1024];string Levelstr = LevelToString(level).c_str();string Timestr = GetTime().c_str();string Idstr = to_string(getpid());snprintf(left, sizeof(left), "[%s][%s][%s] ",Levelstr.c_str(), Timestr.c_str(), Idstr.c_str());va_list args;va_start(args, format);char right[1024];vsnprintf(right, sizeof(right), format, args);string logmessage = left;logmessage += right;WriteLog(level, logmessage);va_end(args);}~Log(){pthread_mutex_destroy(&_log_mutex);};private:int style;string filename;pthread_mutex_t _log_mutex;
};Log lg;class Conf
{
public:Conf(){lg.SwitchStyle(Screen);}~Conf(){}
};Conf conf;

1.3 InetAddr.hpp(管理sockaddr_in相关信息)

#pragma once#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>class InetAddr
{
public:InetAddr(struct sockaddr_in sock):_sock(sock){}std::string Ip(){return inet_ntoa(_sock.sin_addr);}uint16_t Port(){return ntohs(_sock.sin_port);}std::string PrintDebug(){std::string info = "[";info += Ip();info += ":";info += std::to_string(Port());info += "]";info += "# ";return info;}~InetAddr(){}private:struct sockaddr_in _sock;
};

1.4 NoCopy.hpp(防拷贝)

#pragma onceclass Nocopy
{
public:Nocopy() {}~Nocopy() {}Nocopy(const Nocopy&) = delete;const Nocopy& operator=(const Nocopy&) = delete;
};

1.5 UdpServer.hpp(服务端封装)

#pragma once#include <string>
#include "Comm.hpp"
#include "Nocopy.hpp"
#include "InetAddr.hpp"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <string.h>
#include <errno.h>const static int defaultfd = -1;
const static uint16_t defaultport = 8888;
const static int defaultsize = 1024;// 服务器的IP不应该是固定的,而是任意的
class UdpServer : public Nocopy
{
public:UdpServer(uint16_t port = defaultport):_port(port),_sockfd(defaultfd){}void Init(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){lg.LogMessage(Fatal, "create socket fail\n");exit(Socket_err);}lg.LogMessage(Info, "create socket success , sockfd : %d\n", _sockfd);// 2、绑定,指定网络信息,协议家族,IP地址,端口号,结构体填充,设置到内核struct sockaddr_in ServerSockaddr;memset(&ServerSockaddr,0,sizeof(ServerSockaddr));ServerSockaddr.sin_family = AF_INET;ServerSockaddr.sin_port = htons(_port);ServerSockaddr.sin_addr.s_addr = INADDR_ANY; // 就是0socklen_t len = sizeof(ServerSockaddr);int n = ::bind(_sockfd, (struct sockaddr*)&ServerSockaddr, len);if (n == -1){// cout << inet_ntoa(ServerSockaddr.sin_addr) << ":" << ntohs(ServerSockaddr.sin_port) << endl;lg.LogMessage(Fatal, "bind fail , errno : %d , %s\n", errno , strerror(errno));exit(Bind_err);}lg.LogMessage(Info, "bind success , sockfd : %d\n", _sockfd);}void Start(){char inbuffer[defaultsize];for (;;){struct sockaddr_in ClientSockaddr;socklen_t ClientLen = sizeof(ClientSockaddr);// 接收数据ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&ClientSockaddr, &ClientLen);if (n > 0){inbuffer[n] = 0;InetAddr inetaddr(ClientSockaddr);cout << inetaddr.PrintDebug() << inbuffer << endl;// 发送数据sendto(_sockfd, &inbuffer, sizeof(inbuffer), 0, (struct sockaddr*)&ClientSockaddr, ClientLen);}}}~UdpServer(){close(_sockfd);}private:// std::string _ip;uint16_t _port;int _sockfd;
};

1.6 Main.cpp(服务端)

#include "UdpServer.hpp"
#include <iostream>
#include <unistd.h>
#include <string>
#include <memory>using namespace std;void Usage(const string& proc)
{// cout << proc << " localip localport\n" << endl;cout << proc << " localport\n" << endl;
}// ./udpserver localip localport
int main(int argc, char* argv[])
{// 服务器不能固定ip// if (argc != 3)// {//     Usage(argv[0]);//     exit(1);// }if (argc != 2){Usage(argv[0]);exit(1);}// string ip = argv[1];// uint16_t port = stoi(argv[2]);// unique_ptr<UdpServer> uq = make_unique<UdpServer>(ip,port);uint16_t port = stoi(argv[1]);unique_ptr<UdpServer> uq = make_unique<UdpServer>(port);uq->Init();uq->Start();return 0;
}

1.7 UdpClient.cpp(客户端)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>using namespace std;void Usage(const string& proc)
{std::cout << proc << " serverip serverport\n" << endl;
}// ./udpclient serverip serverport
int main(int argc, char* argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int clientfd = socket(AF_INET, SOCK_DGRAM, 0);if (clientfd < 0){cout << "create socket fail\n" << endl;exit(1);}cout << "create socket success : " << clientfd << endl;struct sockaddr_in ServerSocket;memset(&ServerSocket,0,sizeof(ServerSocket));socklen_t Slen = sizeof(ServerSocket);ServerSocket.sin_family = AF_INET;ServerSocket.sin_addr.s_addr = inet_addr(argv[1]);ServerSocket.sin_port = htons(stoi(argv[2]));while (1){string inbuffer;cout << "Please Enter# ";getline(cin,inbuffer);int n = sendto(clientfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr*)&ServerSocket, Slen);if (n < 0){cout << "sendto fail , errno : " << errno <<  ", error : " << strerror(errno) << endl;break;}else{cout << inbuffer << endl;struct sockaddr_in temp;socklen_t tlen = sizeof(temp);char buffer[1024];int m = recvfrom(clientfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&temp, &tlen);if(m > 0){buffer[m] = 0;cout << "Server say#" << buffer << endl;}else{break;}}}close(clientfd);return 0;
}

二、UdpServerExecute(客户端发送指令服务端执行后返回执行结果)

2.1 Comm.hpp(公共数据)

#pragma onceenum {Socket_err = 1,Bind_err,Accept_err,Recvfrom_err
};

2.2 Log.hpp(日志)

#pragma once#include <pthread.h>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>using namespace std;// 日志等级
enum
{Debug = 0, // 调试Info,      // 正常Warning,   // 警告Error,     // 错误,但程序并未直接退出Fatal      // 程序直接挂掉
};enum
{Screen = 10, // 打印到显示器上OneFile,     // 打印到一个文件中ClassFile    // 按照日志等级打印到不同的文件中
};string LevelToString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Unknow";}
}const char* default_filename = "log.";
const int default_style = Screen;
const char* defaultdir = "log";class Log
{
public:Log(): style(default_style), filename(default_filename){// mkdir(defaultdir,0775);pthread_mutex_init(&_log_mutex, nullptr);}void SwitchStyle(int sty){style = sty;}void WriteLogToOneFile(const string& logname, const string& logmessage){int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd == -1)return;pthread_mutex_lock(&_log_mutex);write(fd, logmessage.c_str(), logmessage.size());pthread_mutex_unlock(&_log_mutex);close(fd);}void WriteLogToClassFile(const string& levelstr, const string& logmessage){mkdir(defaultdir, 0775);string name = defaultdir;name += "/";name += filename;name += levelstr;WriteLogToOneFile(name, logmessage);}void WriteLog(int level, const string& logmessage){switch (style){case Screen:{pthread_mutex_lock(&_log_mutex);cout << logmessage;pthread_mutex_unlock(&_log_mutex);}break;case OneFile:WriteLogToClassFile("All", logmessage);break;case ClassFile:WriteLogToClassFile(LevelToString(level), logmessage);break;default:break;}}string GetTime(){time_t CurrentTime = time(nullptr);struct tm* curtime = localtime(&CurrentTime);char time[128];// localtime 的年是从1900开始的,所以要加1900, 月是从0开始的所以加1snprintf(time, sizeof(time), "%d-%d-%d %d:%d:%d",curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday,curtime->tm_hour, curtime->tm_min, curtime->tm_sec);return time;return "";}void LogMessage(int level, const char* format, ...){char left[1024];string Levelstr = LevelToString(level).c_str();string Timestr = GetTime().c_str();string Idstr = to_string(getpid());snprintf(left, sizeof(left), "[%s][%s][%s] ",Levelstr.c_str(), Timestr.c_str(), Idstr.c_str());va_list args;va_start(args, format);char right[1024];vsnprintf(right, sizeof(right), format, args);string logmessage = left;logmessage += right;WriteLog(level, logmessage);va_end(args);}~Log(){pthread_mutex_destroy(&_log_mutex);};private:int style;string filename;pthread_mutex_t _log_mutex;
};Log lg;class Conf
{
public:Conf(){lg.SwitchStyle(Screen);}~Conf(){}
};Conf conf;

2.3 InetAddr.hpp(管理sockaddr_in相关信息)

#pragma once#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>class InetAddr
{
public:InetAddr(struct sockaddr_in sock):_sock(sock){}std::string Ip(){return inet_ntoa(_sock.sin_addr);}uint16_t Port(){return ntohs(_sock.sin_port);}std::string PrintDebug(){std::string info = "[";info += Ip();info += ":";info += std::to_string(Port());info += "]";info += "# ";return info;}~InetAddr(){}private:struct sockaddr_in _sock;
};

2.4 NoCopy.hpp(防拷贝)

#pragma onceclass Nocopy
{
public:Nocopy() {}~Nocopy() {}Nocopy(const Nocopy&) = delete;const Nocopy& operator=(const Nocopy&) = delete;
};

2.5 UdpServer.hpp(服务端封装)

#pragma once#include "Comm.hpp"
#include "Nocopy.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <string>
#include <errno.h>
#include <functional>const static int defaultfd = -1;
const static uint16_t defaultport = 8888;
const static int defaultsize = 1024;using cb_t = function<string(string)>;// 服务器的IP不应该是固定的,而是任意的
class UdpServer : public Nocopy
{
public:UdpServer(cb_t OnMessage,uint16_t port = defaultport):_port(port),_sockfd(defaultfd),_OnMessage(OnMessage){}void Init(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){lg.LogMessage(Fatal, "create socket fail\n");exit(Socket_err);}lg.LogMessage(Info, "create socket success , sockfd : %d\n", _sockfd);// 2、绑定,指定网络信息,协议家族,IP地址,端口号,结构体填充,设置到内核struct sockaddr_in ServerSockaddr;memset(&ServerSockaddr,0,sizeof(ServerSockaddr));ServerSockaddr.sin_family = AF_INET;ServerSockaddr.sin_port = htons(_port);ServerSockaddr.sin_addr.s_addr = INADDR_ANY; // 就是0socklen_t len = sizeof(ServerSockaddr);int n = ::bind(_sockfd, (struct sockaddr*)&ServerSockaddr, len);if (n == -1){// cout << inet_ntoa(ServerSockaddr.sin_addr) << ":" << ntohs(ServerSockaddr.sin_port) << endl;lg.LogMessage(Fatal, "bind fail , errno : %d , %s\n", errno , strerror(errno));exit(Bind_err);}lg.LogMessage(Info, "bind success , sockfd : %d\n", _sockfd);}void Start(){char inbuffer[defaultsize];for (;;){struct sockaddr_in ClientSockaddr;socklen_t ClientLen = sizeof(ClientSockaddr);// 接收数据ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&ClientSockaddr, &ClientLen);if (n > 0){inbuffer[n] = 0;InetAddr inetaddr(ClientSockaddr);cout << inetaddr.PrintDebug() << inbuffer<< endl;// 发送数据string response = _OnMessage(inbuffer);sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&ClientSockaddr, ClientLen);}}}~UdpServer(){close(_sockfd);}private:// std::string _ip;uint16_t _port;int _sockfd;cb_t _OnMessage;
};

2.6 Main.cpp(服务端)

#include "UdpServer.hpp"
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <memory>using namespace std;void Usage(const string& proc)
{// cout << proc << " localip localport\n" << endl;cout << proc << " localport\n" << endl;
}string DefalutMessage(string message)
{return message + " [I've already gotten the information.]";
}vector<string> black_list{"rm" , "cp" , "mv" , "top" , "vim" , "vi" , "nano" , "kill"
};bool IsSafe(const string &message)
{size_t pos = 0;for(auto v : black_list){pos = message.find(v);if(pos != string::npos)return false;}return true;
}string ExecuteCommand(string message)
{if(!IsSafe(message)){return "Prohibited Command";}FILE* fp = popen(message.c_str(),"r");if(!fp)return "Unknown command";char buffer[1024];string response;while(1){if(!fgets(buffer,1023,fp))break;response+=buffer;}fclose(fp);return response;
}// ./udpserver localip localport
int main(int argc, char* argv[])
{if (argc != 2){Usage(argv[0]);exit(1);}uint16_t port = stoi(argv[1]);// unique_ptr<UdpServer> uq = make_unique<UdpServer>(DefalutMessage,port);unique_ptr<UdpServer> uq = make_unique<UdpServer>(ExecuteCommand,port);uq->Init();uq->Start();return 0;
}

2.7 UdpClient.cpp(客户端)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>using namespace std;void Usage(const string& proc)
{std::cout << proc << " serverip serverport\n" << endl;
}// ./udpclient serverip serverport
int main(int argc, char* argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int clientfd = socket(AF_INET, SOCK_DGRAM, 0);if (clientfd < 0){cout << "create socket fail\n" << endl;exit(1);}cout << "create socket success : " << clientfd << endl;struct sockaddr_in ServerSocket;memset(&ServerSocket,0,sizeof(ServerSocket));socklen_t Slen = sizeof(ServerSocket);ServerSocket.sin_family = AF_INET;ServerSocket.sin_addr.s_addr = inet_addr(argv[1]);ServerSocket.sin_port = htons(stoi(argv[2]));while (1){string inbuffer;cout << "Please Enter# ";getline(cin,inbuffer);int n = sendto(clientfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr*)&ServerSocket, Slen);if (n < 0){cout << "sendto fail , errno : " << errno <<  ", error : " << strerror(errno) << endl;break;}else{struct sockaddr_in temp;socklen_t tlen = sizeof(temp);char buffer[1024];int m = recvfrom(clientfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&temp, &tlen);if(m > 0){buffer[m] = 0;cout << "Server say# " << buffer << endl;}else{break;}}}close(clientfd);return 0;
}

三、UdpServerChat(实现简单的聊天室)

3.1 Comm.hpp(公共数据)

#pragma onceenum {Socket_err = 1,Bind_err,Accept_err,Recvfrom_err
};

3.2 Log.hpp(日志)

#pragma once#include <pthread.h>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>using namespace std;// 日志等级
enum
{Debug = 0, // 调试Info,      // 正常Warning,   // 警告Error,     // 错误,但程序并未直接退出Fatal      // 程序直接挂掉
};enum
{Screen = 10, // 打印到显示器上OneFile,     // 打印到一个文件中ClassFile    // 按照日志等级打印到不同的文件中
};string LevelToString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Unknow";}
}const char* default_filename = "log.";
const int default_style = Screen;
const char* defaultdir = "log";class Log
{
public:Log(): style(default_style), filename(default_filename){// mkdir(defaultdir,0775);pthread_mutex_init(&_log_mutex, nullptr);}void SwitchStyle(int sty){style = sty;}void WriteLogToOneFile(const string& logname, const string& logmessage){int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd == -1)return;pthread_mutex_lock(&_log_mutex);write(fd, logmessage.c_str(), logmessage.size());pthread_mutex_unlock(&_log_mutex);close(fd);}void WriteLogToClassFile(const string& levelstr, const string& logmessage){mkdir(defaultdir, 0775);string name = defaultdir;name += "/";name += filename;name += levelstr;WriteLogToOneFile(name, logmessage);}void WriteLog(int level, const string& logmessage){switch (style){case Screen:{pthread_mutex_lock(&_log_mutex);cout << logmessage;pthread_mutex_unlock(&_log_mutex);}break;case OneFile:WriteLogToClassFile("All", logmessage);break;case ClassFile:WriteLogToClassFile(LevelToString(level), logmessage);break;default:break;}}string GetTime(){time_t CurrentTime = time(nullptr);struct tm* curtime = localtime(&CurrentTime);char time[128];// localtime 的年是从1900开始的,所以要加1900, 月是从0开始的所以加1snprintf(time, sizeof(time), "%d-%d-%d %d:%d:%d",curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday,curtime->tm_hour, curtime->tm_min, curtime->tm_sec);return time;return "";}void LogMessage(int level, const char* format, ...){char left[1024];string Levelstr = LevelToString(level).c_str();string Timestr = GetTime().c_str();string Idstr = to_string(getpid());snprintf(left, sizeof(left), "[%s][%s][%s] ",Levelstr.c_str(), Timestr.c_str(), Idstr.c_str());va_list args;va_start(args, format);char right[1024];vsnprintf(right, sizeof(right), format, args);string logmessage = left;logmessage += right;WriteLog(level, logmessage);va_end(args);}~Log(){pthread_mutex_destroy(&_log_mutex);};private:int style;string filename;pthread_mutex_t _log_mutex;
};Log lg;class Conf
{
public:Conf(){lg.SwitchStyle(Screen);}~Conf(){}
};Conf conf;

3.3 InetAddr.hpp(管理sockaddr_in相关信息)

#pragma once#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>class InetAddr
{
public:InetAddr(struct sockaddr_in sock):_sock(sock){}std::string Ip(){return inet_ntoa(_sock.sin_addr);}uint16_t Port(){return ntohs(_sock.sin_port);}std::string PrintDebug(){std::string info = "[";info += Ip();info += ":";info += std::to_string(Port());info += "]";info += "# ";return info;}~InetAddr(){}private:struct sockaddr_in _sock;
};

3.4 NoCopy.hpp(防拷贝)

#pragma onceclass Nocopy
{
public:Nocopy() {}~Nocopy() {}Nocopy(const Nocopy&) = delete;const Nocopy& operator=(const Nocopy&) = delete;
};

3.5 Lockguard.hpp(自动管理锁)

#pragma once#include <iostream>class Mutex
{
public:Mutex(pthread_mutex_t* lock):pmutex(lock){}void Lock(){pthread_mutex_lock(pmutex);}void Unlock(){pthread_mutex_unlock(pmutex);}~Mutex(){}
public:pthread_mutex_t* pmutex;
};class LockGuard
{
public:LockGuard(pthread_mutex_t* lock):mutex(lock){mutex.Lock();}~LockGuard(){mutex.Unlock();}
public:Mutex mutex;
};

3.6 Thread.hpp(封装线程)

#pragma once#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>using namespace std;// typedef function<void()> func_t;
template<class T>
using func_t = function<void(T&)>;template<class T>
class Thread
{
public:Thread(const string &threadname, func_t<T> func ,const T& data): _pid(0), _threadname(threadname), _func(func), isrunning(false) , _data(data){}// 线程需要执行的函数static void *ThreadRoutine(void *arg){Thread *pt = (Thread *)arg;pt->_func(pt->_data);return nullptr;}// 线程开创建并执行bool Start(){int n = pthread_create(&_pid, nullptr, ThreadRoutine, this);if (n == 0){isrunning = true;// cout << "is strat , is running : " << isrunning << endl;return true;}else{return false;}}// 线程等待bool Join(){if(!isrunning)  return false;return pthread_join(_pid, nullptr);}bool IsRunning(){return isrunning;}string ThreadName(){return _threadname;}~Thread(){}private:pthread_t _pid;string _threadname;bool isrunning;func_t<T> _func;T _data;
};

3.7 ThreadPool.hpp(线程池)

#pragma once#include <string>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Thread.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"using namespace std;const int default_threadnum = 3;class ThreadDate
{
public:ThreadDate(const string &name): threadname(name){}~ThreadDate(){}public:string threadname;
};template <class T>
class ThreadPool
{
public:static ThreadPool *GetInstance(){if (_psl == nullptr){LockGuard lock(&_sig_lock);if (_psl == nullptr){lg.LogMessage(Info, "create singleton is success\n");_psl = new ThreadPool<T>();}}return _psl;}static void DelInstance(){if (_psl){LockGuard lock(&_sig_lock);if (_psl){delete _psl;_psl = nullptr;lg.LogMessage(Info, "destroy singleton is success\n");}}}// 启动所有线程bool Start(){for (auto &t : _threads){t.Start();lg.LogMessage(Info, "%s , is running...\n", t.ThreadName().c_str());}return true;}// 等待所有线程终止void Join(){for (auto &t : _threads){t.Join();}}// 线程等待当前条件变量void Thread_Wait(const ThreadDate &td){pthread_cond_wait(&_cond, &_mutex);lg.LogMessage(Debug, "no task , %s is sleeping...\n", td.threadname.c_str());}// 唤醒当前条件变量下的某个线程void Thread_Wakeup(){pthread_cond_signal(&_cond);}// 添加任务bool Push(T &in){LockGuard lockguard(&_mutex);_q.push(in);Thread_Wakeup();return true;}// 线程需要执行的回调函数void ThreadRun(const ThreadDate &td){while (1){T t;// 取任务{LockGuard lockguard(&_mutex);while (_q.empty()){Thread_Wait(td);lg.LogMessage(Debug, "haven task , %s is wakeup\n", td.threadname.c_str());}t = _q.front();_q.pop();}// 执行任务t();// lg.LogMessage(Debug, "%s handler task %s done , result is %s\n",//               td.threadname.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str());}}private:ThreadPool(int num = default_threadnum): _threadnum(num){// 初始化锁和条件变量pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);// 创建线程for (int i = 0; i < _threadnum; i++){string threadname = "thread-" + to_string(i + 1);ThreadDate td(threadname);// 由于Thread执行的是线程池的类内函数,而Thread调用的函数中并没有this指针// 所以这里就使用bind函数,调整一下Thread调用函数的参数,使函数可以多接收一个参数_threads.push_back(Thread<ThreadDate>(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td));lg.LogMessage(Info, "%s is create\n", threadname.c_str());}}// 销毁锁和条件变量~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}ThreadPool(const ThreadPool&) = delete;const ThreadPool& operator=(const ThreadPool&) = delete;private:queue<T> _q;vector<Thread<ThreadDate>> _threads;int _threadnum;static ThreadPool<T> *_psl;static pthread_mutex_t _sig_lock;pthread_mutex_t _mutex;pthread_cond_t _cond;
};template <class T>
ThreadPool<T> *ThreadPool<T>::_psl = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::_sig_lock = PTHREAD_MUTEX_INITIALIZER;

3.8 Daemon.hpp(使进程变为守护进程)

#pragma once#include <signal.h>
#include <unistd.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <fcntl.h>const char* root = "/";
const char* dev_null = "/dev/null";bool Daemon(bool nochdir, bool noclose)
{// 1、忽略可能引起程序异常退出的信号 SIGCHLD SIGPIPEsignal(SIGCHLD,SIG_IGN);signal(SIGPIPE,SIG_IGN);// 2、创建子进程,让父进程退出,使得子进程不成为组长pid_t pid = fork();if(pid > 0) exit(0);// 3、设置自己成为一个新的会画,setsidsetsid();// 4、每一个进程都有自己的CWD(当前工作路径),是否将当前进程的CWD改为根目录// ​	改为根目录以后,进程可以以绝对路径的方式找到操作系统中的文件if(nochdir)chdir(root);// 5、变成守护进程以后,就不需要与用户的输入、输出和错误进行关联了// ​	可以将它们全部关闭,但难免服务器中会有输入、输出和错误// ​	向关闭的文件描述符中写入可能会导致进程退出// ​	所以这里将它们关闭不是最优解,而是将它们重定向到/dev/null中// ​	因为写入到/dev/null的数据会被直接丢弃,而从/dev/null读取信息,会默认读取到文件结尾if(noclose){int fd = open(dev_null,O_RDWR);if(fd > 0){dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);}}else  // 不推荐{close(0);close(1);close(2);}return true;
}

3.9 UdpServer.hpp(服务端封装)

#pragma once#include "Comm.hpp"
#include "Nocopy.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <string>
#include <errno.h>
#include <functional>
#include <pthread.h>const static int defaultfd = -1;
const static uint16_t defaultport = 8888;
const static int defaultsize = 1024;using task_t = function<void(void)>;// 服务器的IP不应该是固定的,而是任意的
class UdpServer : public Nocopy
{
public:UdpServer(uint16_t port = defaultport):_port(port),_sockfd(defaultfd){}void Init(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){lg.LogMessage(Fatal, "create socket fail\n");exit(Socket_err);}lg.LogMessage(Info, "create socket success , sockfd : %d\n", _sockfd);// 2、绑定,指定网络信息,协议家族,IP地址,端口号,结构体填充,设置到内核struct sockaddr_in ServerSockaddr;memset(&ServerSockaddr,0,sizeof(ServerSockaddr));ServerSockaddr.sin_family = AF_INET;ServerSockaddr.sin_port = htons(_port);ServerSockaddr.sin_addr.s_addr = INADDR_ANY; // 就是0socklen_t len = sizeof(ServerSockaddr);int n = ::bind(_sockfd, (struct sockaddr*)&ServerSockaddr, len);if (n == -1){// cout << inet_ntoa(ServerSockaddr.sin_addr) << ":" << ntohs(ServerSockaddr.sin_port) << endl;lg.LogMessage(Fatal, "bind fail , errno : %d , %s\n", errno , strerror(errno));exit(Bind_err);}lg.LogMessage(Info, "bind success , sockfd : %d\n", _sockfd);pthread_mutex_init(&_mutex,nullptr);ThreadPool<task_t>::GetInstance()->Start();}void AddOnlineUser(InetAddr addr){LockGuard lock(&_mutex);for(auto tmp : _online_user){if(tmp == addr)return;}_online_user.push_back(addr);}void Route(int sockfd ,const string& message){LockGuard lock(&_mutex);for(auto &tmp : _online_user){sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&tmp.GetAddr(), sizeof(tmp));}}void Start(){char inbuffer[defaultsize];for (;;){struct sockaddr_in ClientSockaddr;socklen_t ClientLen = sizeof(ClientSockaddr);// 接收数据ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&ClientSockaddr, &ClientLen);if (n > 0){inbuffer[n] = 0;InetAddr inetaddr(ClientSockaddr);AddOnlineUser(inetaddr);cout << inetaddr.PrintDebug() << inbuffer<< endl;string message = inbuffer;task_t task = bind(&UdpServer::Route,this,_sockfd,message);ThreadPool<task_t>::GetInstance()->Push(task);}}}~UdpServer(){pthread_mutex_destroy(&_mutex);close(_sockfd);}private:// std::string _ip;uint16_t _port;int _sockfd;vector<InetAddr> _online_user;pthread_mutex_t _mutex;
};

3.10 Main.cpp(服务端)

#include "UdpServer.hpp"
#include "Daemon.hpp"
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <memory>using namespace std;void Usage(const string& proc)
{// cout << proc << " localip localport\n" << endl;cout << proc << " localport\n" << endl;
}// ./udpserver localip localport
int main(int argc, char* argv[])
{if (argc != 2){Usage(argv[0]);exit(1);}uint16_t port = stoi(argv[1]);Daemon(true,true);lg.SwitchStyle(OneFile);// unique_ptr<UdpServer> uq = make_unique<UdpServer>(DefalutMessage,port);unique_ptr<UdpServer> uq = make_unique<UdpServer>(port);uq->Init();uq->Start();return 0;
}

3.11 UdpClient.cpp(客户端)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Thread.hpp"
#include "LockGuard.hpp"
#include "InetAddr.hpp"using namespace std;class ThreadDate
{
public:ThreadDate(int sockfd, InetAddr addr): _sockfd(sockfd), _addr(addr){}~ThreadDate(){}public:int _sockfd;struct InetAddr _addr;
};void Usage(const string &proc)
{cout << proc << " serverip serverport\n"<< endl;
}void RecverRoutine(ThreadDate td)
{struct sockaddr_in temp;socklen_t tlen = sizeof(temp);char inbuffer[1024];while (1){int m = recvfrom(td._sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&temp, &tlen);if (m > 0){inbuffer[m] = 0;cerr << td._addr.PrintDebug() << inbuffer << endl;}}
}void SenderRoutine(ThreadDate td)
{string buffer;while (1){cout << "Please Enter# ";getline(cin, buffer);int n = sendto(td._sockfd, buffer.c_str(), buffer.size(), 0, (struct sockaddr *)&td._addr.GetAddr(), sizeof(td._addr));if (n < 0)break;}
}// ./udpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int clientfd = socket(AF_INET, SOCK_DGRAM, 0);if (clientfd < 0){cout << "create socket fail\n"<< endl;exit(1);}cout << "create socket success : " << clientfd << endl;struct sockaddr_in ServerSocket;memset(&ServerSocket, 0, sizeof(ServerSocket));socklen_t Slen = sizeof(ServerSocket);ServerSocket.sin_family = AF_INET;ServerSocket.sin_addr.s_addr = inet_addr(argv[1]);ServerSocket.sin_port = htons(stoi(argv[2]));ThreadDate td(clientfd, ServerSocket);Thread<ThreadDate> sender("sender", SenderRoutine, td);Thread<ThreadDate> recver("recver", RecverRoutine, td);sender.Start();recver.Start();sender.Join();recver.Join();close(clientfd);return 0;
}

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

在这里插入图片描述

http://www.lryc.cn/news/583578.html

相关文章:

  • 分库分表之实战-sharding-JDBC水平分库+分表后:查询与删除操作实战
  • Android Notification 通过增加addAction 跳转回Service重新执行逻辑
  • 海信IP501H_GK6323处理器免拆卡刷包和线刷救砖包_当贝纯净版
  • LLM 在预测下一个词的时候是怎么计算向量的,说明详细过程
  • 数据库技术体系及场景选型方案
  • RNN及其变体的概念和案例
  • 数据一致性解决方案总结
  • 软件发布的完整流程梳理
  • brainstorm MEG处理流程
  • 【科研绘图系列】R语言绘制解剖图
  • 【leetcode】2235. 两整数相加
  • 本地Qwen中医问诊小程序系统开发
  • softmax
  • PyTorch数据准备:从基础Dataset到高效DataLoader
  • C#字符串相关库函数运用梳理总结 + 正则表达式详解
  • 基于物联网的智能家居控制系统设计与实现
  • 17-C#封装,继承,多态与重载
  • 【AIGC】讯飞长录音ASR转写,使用JAVA实现科大讯飞语音服务ASR转录功能:完整指南
  • JavaScript基础篇——第五章 对象(最终篇)
  • NLP革命二十年:从规则驱动到深度学习的跃迁
  • LLaMA-Omni 深度解析:打开通往无缝人机语音交互的大门
  • pip install av安装av库失败解决方法
  • Celery Django配置
  • 存储服务一NFS文件存储概述
  • Mysql基于belog恢复数据
  • 精准医疗,AR 锚定球囊扩张导管为健康护航​
  • 基于 Spark MLlib 的推荐系统实现
  • 打破传统,开启 AR 智慧课堂​
  • langchain从入门到精通(四十一)——基于ReACT架构的Agent智能体设计与实现
  • 基于BRPC构建高性能HTTP/2服务实战指南