
目录
- 一、UdpServerSever(客户端发送信息,服务端直接返回信息)
-
- [1.1 Comm.hpp(公共数据)](#1.1 Comm.hpp(公共数据))
- [1.2 Log.hpp(日志)](#1.2 Log.hpp(日志))
- [1.3 InetAddr.hpp(管理sockaddr_in相关信息)](#1.3 InetAddr.hpp(管理sockaddr_in相关信息))
- [1.4 NoCopy.hpp(防拷贝)](#1.4 NoCopy.hpp(防拷贝))
- [1.5 UdpServer.hpp(服务端封装)](#1.5 UdpServer.hpp(服务端封装))
- [1.6 Main.cpp(服务端)](#1.6 Main.cpp(服务端))
- [1.7 UdpClient.cpp(客户端)](#1.7 UdpClient.cpp(客户端))
- 二、UdpServerExecute(客户端发送指令服务端执行后返回执行结果)
-
- [2.1 Comm.hpp(公共数据)](#2.1 Comm.hpp(公共数据))
- [2.2 Log.hpp(日志)](#2.2 Log.hpp(日志))
- [2.3 InetAddr.hpp(管理sockaddr_in相关信息)](#2.3 InetAddr.hpp(管理sockaddr_in相关信息))
- [2.4 NoCopy.hpp(防拷贝)](#2.4 NoCopy.hpp(防拷贝))
- [2.5 UdpServer.hpp(服务端封装)](#2.5 UdpServer.hpp(服务端封装))
- [2.6 Main.cpp(服务端)](#2.6 Main.cpp(服务端))
- [2.7 UdpClient.cpp(客户端)](#2.7 UdpClient.cpp(客户端))
- 三、UdpServerChat(实现简单的聊天室)
-
- [3.1 Comm.hpp(公共数据)](#3.1 Comm.hpp(公共数据))
- [3.2 Log.hpp(日志)](#3.2 Log.hpp(日志))
- [3.3 InetAddr.hpp(管理sockaddr_in相关信息)](#3.3 InetAddr.hpp(管理sockaddr_in相关信息))
- [3.4 NoCopy.hpp(防拷贝)](#3.4 NoCopy.hpp(防拷贝))
- [3.5 Lockguard.hpp(自动管理锁)](#3.5 Lockguard.hpp(自动管理锁))
- [3.6 Thread.hpp(封装线程)](#3.6 Thread.hpp(封装线程))
- [3.7 ThreadPool.hpp(线程池)](#3.7 ThreadPool.hpp(线程池))
- [3.8 Daemon.hpp(使进程变为守护进程)](#3.8 Daemon.hpp(使进程变为守护进程))
- [3.9 UdpServer.hpp(服务端封装)](#3.9 UdpServer.hpp(服务端封装))
- [3.10 Main.cpp(服务端)](#3.10 Main.cpp(服务端))
- [3.11 UdpClient.cpp(客户端)](#3.11 UdpClient.cpp(客户端))
- 结尾
一、UdpServerSever(客户端发送信息,服务端直接返回信息)
1.1 Comm.hpp(公共数据)
cpp
#pragma once
enum {
Socket_err = 1,
Bind_err,
Accept_err,
Recvfrom_err
};
1.2 Log.hpp(日志)
cpp
#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开始的所以加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相关信息)
cpp
#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(防拷贝)
cpp
#pragma once
class Nocopy
{
public:
Nocopy() {}
~Nocopy() {}
Nocopy(const Nocopy&) = delete;
const Nocopy& operator=(const Nocopy&) = delete;
};
1.5 UdpServer.hpp(服务端封装)
cpp
#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; // 就是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(服务端)
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(客户端)
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(公共数据)
cpp
#pragma once
enum {
Socket_err = 1,
Bind_err,
Accept_err,
Recvfrom_err
};
2.2 Log.hpp(日志)
cpp
#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开始的所以加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相关信息)
cpp
#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(防拷贝)
cpp
#pragma once
class Nocopy
{
public:
Nocopy() {}
~Nocopy() {}
Nocopy(const Nocopy&) = delete;
const Nocopy& operator=(const Nocopy&) = delete;
};
2.5 UdpServer.hpp(服务端封装)
cpp
#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; // 就是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(服务端)
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(客户端)
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(公共数据)
cpp
#pragma once
enum {
Socket_err = 1,
Bind_err,
Accept_err,
Recvfrom_err
};
3.2 Log.hpp(日志)
cpp
#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开始的所以加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相关信息)
cpp
#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(防拷贝)
cpp
#pragma once
class Nocopy
{
public:
Nocopy() {}
~Nocopy() {}
Nocopy(const Nocopy&) = delete;
const Nocopy& operator=(const Nocopy&) = delete;
};
3.5 Lockguard.hpp(自动管理锁)
cpp
#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(封装线程)
cpp
#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(线程池)
cpp
#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(使进程变为守护进程)
cpp
#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 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(服务端封装)
cpp
#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; // 就是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(服务端)
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(客户端)
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;
}
结尾
如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹
