【Linux | 网络】socket编程 - 使用UDP实现服务端向客户端提供简单的服务_linux版本zynq 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(实现简单的聊天室)
- 结尾
一、UdpServerSever(客户端发送信息,服务端直接返回信息)
1.1 Comm.hpp(公共数据)
#pragma onceenum { Socket_err = 1, Bind_err, Accept_err, Recvfrom_err};
1.2 Log.hpp(日志)
#pragma once#include #include #include #include #include #include #include #include #include #include #include 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开始的所以加1 snprintf(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 #include #include /* See NOTES */#include #include 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 #include \"Comm.hpp\"#include \"Nocopy.hpp\"#include \"InetAddr.hpp\"#include /* See NOTES */#include #include #include #include \"Log.hpp\"#include #include 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; // 就是0 socklen_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 #include #include #include using namespace std;void Usage(const string& proc){ // cout << proc << \" localip localport\\n\" << endl; cout << proc << \" localport\\n\" << endl;}// ./udpserver localip localportint 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 uq = make_unique(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 #include #include #include #include #include #include /* See NOTES */#include #include #include #include using namespace std;void Usage(const string& proc){ std::cout << proc << \" serverip serverport\\n\" << endl;}// ./udpclient serverip serverportint 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 #include #include #include #include #include #include #include #include #include #include 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开始的所以加1 snprintf(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 #include #include /* See NOTES */#include #include 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 /* See NOTES */#include #include #include #include #include #include #include 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; // 就是0 socklen_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 #include #include #include #include #include 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 localportint main(int argc, char* argv[]){ if (argc != 2) { Usage(argv[0]); exit(1); } uint16_t port = stoi(argv[1]); // unique_ptr uq = make_unique(DefalutMessage,port); unique_ptr<UdpServer> uq = make_unique<UdpServer>(ExecuteCommand,port); uq->Init(); uq->Start(); return 0;}
2.7 UdpClient.cpp(客户端)
#include #include #include #include #include #include #include /* See NOTES */#include #include #include #include using namespace std;void Usage(const string& proc){ std::cout << proc << \" serverip serverport\\n\" << endl;}// ./udpclient serverip serverportint 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 #include #include #include #include #include #include #include #include #include #include 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开始的所以加1 snprintf(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 #include #include /* See NOTES */#include #include 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 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 #include #include #include using namespace std;// typedef function 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 #include #include #include #include #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 #include #include #include #include const char* root = \"/\";const char* dev_null = \"/dev/null\";bool Daemon(bool nochdir, bool noclose){ // 1、忽略可能引起程序异常退出的信号 SIGCHLD SIGPIPE signal(SIGCHLD,SIG_IGN); signal(SIGPIPE,SIG_IGN); // 2、创建子进程,让父进程退出,使得子进程不成为组长 pid_t pid = fork(); if(pid > 0) exit(0); // 3、设置自己成为一个新的会画,setsid setsid(); // 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 /* See NOTES */#include #include #include #include #include #include #include #include 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; // 就是0 socklen_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 #include #include #include #include #include using namespace std;void Usage(const string& proc){ // cout << proc << \" localip localport\\n\" << endl; cout << proc << \" localport\\n\" << endl;}// ./udpserver localip localportint 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 uq = make_unique(DefalutMessage,port); unique_ptr<UdpServer> uq = make_unique<UdpServer>(port); uq->Init(); uq->Start(); return 0;}
3.11 UdpClient.cpp(客户端)
#include #include #include #include #include #include #include /* See NOTES */#include #include #include #include #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 serverportint 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;}
结尾
如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹