目录
服务端
这个TcpServer
类是一个简单的TCP服务器实现,用于监听指定端口上的连接请求,并处理客户端的连接和数据交互。
成员变量
-
_port
: 服务器监听的端口号,默认为8080。 -
_listensock
: 监听套接字的文件描述符。 -
_isrunning
: 表示服务器是否正在运行的布尔变量。 -
funcs
: 一个无序映射,用于存储注册的服务函数。键是服务名称,值是对应的服务处理函数。
成员函数
-
构造函数 (
TcpServer
): 接收监听套接字和端口号作为参数,但都有默认值,因此可以无参构造。构造函数仅初始化成员变量。 -
Init(): 初始化服务器,包括创建套接字、设置套接字选项、绑定到指定端口、开始监听等步骤。如果在任何步骤中发生错误,程序会记录错误并退出。
-
Start(): 启动服务器的主循环,接受连接请求。对于每个接受的连接,都会创建一个新的任务并将其添加到线程池中执行。
-
Read(int sockfd): 从给定的套接字读取数据。如果读取成功,返回读取到的字符串;如果读取失败或连接已关闭,记录相应的日志。
-
Routine(int sockfd, InetAddr addr): 处理来自客户端的连接。首先调用默认服务,然后读取客户端发送的数据类型,并根据数据类型调用相应的服务函数。
-
DefaultService(int sockfd, InetAddr &addr): 默认服务函数,向客户端发送可用的服务列表。
-
Service(int sockfd, InetAddr addr): 一个示例服务函数,接收来自客户端的消息并将其回显。
-
RegisterFunc(const std::string &name, callback_t func): 允许用户注册自定义的服务函数。这些函数可以根据从客户端接收到的数据类型被调用。
-
析构函数 (
~TcpServer
): 目前析构函数为空,但在实际应用中,通常会在这里关闭套接字和释放资源。
工作流程
-
服务器通过
Init()
函数进行初始化,包括套接字的创建、绑定和监听。 -
调用
Start()
函数开始接受连接。对于每个接受的连接,都会创建一个新的任务来处理它。 -
在任务中,首先调用
DefaultService
向客户端发送可用的服务列表。 -
然后读取客户端发送的数据类型,并根据该类型调用相应的服务函数。
-
服务函数处理完客户端的请求后,关闭与客户端的连接。
这个类提供了一个基本的TCP服务器框架,用户可以通过注册自定义的服务函数来扩展其功能。注意,这个实现是同步的,每个连接都会占用一个线程来处理,因此在高并发环境下可能需要更高效的IO模型,如异步IO或多线程/多进程模型。
#pragma once
#include "ErrInfo.hpp"
#include "Log.hpp"
#include "NoCopy.hpp"
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <signal.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <unordered_map>
#include "Thread.hpp"
#include "ThreadPool.hpp"
#include"InetAddr.hpp"
#include"LockGuard.hpp"
#include"Translate.hpp"
static const int default_backlog = 5;
static const int DefaultListenSocket = -1;
static const uint16_t DefaultPort = 8080;
using task_t = std::function<void()>;
using callback_t = std::function<void(int, InetAddr&)>;
class TcpServer : public NoCopy
{
public:
TcpServer(int listensocket = DefaultListenSocket, uint16_t port = DefaultPort)
: _port(port),
_listensock(listensocket),
_isrunning(false)
{
}
void Init()
{
// 创建套接字
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
lg.LogMessage(Fatal, "创建套接字失败 错误码:%d 错误信息:%s \n", errno, strerror(errno));
exit(Socket_Err);
}
lg.LogMessage(Info, "创建套接字成功!套接字:%d\n", _listensock);
// 减少bind错误
int opt = 1;
setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
// 填充信息
sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_port = htons(_port);
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
// 进行绑定
int n = bind(_listensock, (sockaddr *)&local, sizeof(local));
if (n != 0)
{
lg.LogMessage(Fatal, "绑定失败! 错误码:%d, 错误信息:%s\n", errno, strerror(errno));
exit(Bind_Err);
}
lg.LogMessage(Info, "绑定成功!\n");
// 设置监听状态
n = listen(_listensock, default_backlog);
if (n != 0)
{
lg.LogMessage(Fatal, "监听失败!错误码:%d,错误信息:%s\n", errno, strerror(errno));
exit(Listen_Err);
}
lg.LogMessage(Info, "监听成功!\n");
ThreadPool<task_t>::GetInstance()->Start();
funcs.insert(std::make_pair("defaultService", std::bind(&TcpServer::DefaultService, this, std::placeholders::_1, std::placeholders::_2)));
}
void Start()
{
_isrunning = true;
signal(SIGCHLD, SIG_IGN);
while (_isrunning)
{
sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, (sockaddr *)&peer, &len);
if (sockfd < 0)
{
lg.LogMessage(Warning, "接受套接字失败, 错误码: %d, 错误信息: %s\n", errno, strerror(errno));
continue;
}
lg.LogMessage(Debug, "接受套接字成功,新的套接字为: %d\n", sockfd);
// 处理信息
task_t t = std::bind(&TcpServer::Routine, this, sockfd, InetAddr(peer));
ThreadPool<task_t>::GetInstance()->Push(t);
}
}
std::string Read(int sockfd)
{
char type[1024];
ssize_t n = read(sockfd, type, sizeof(type) - 1);
if (n > 0)
{
type[n] = 0;
}
else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
{
lg.LogMessage(Info, "client quit...\n");
}
else
{
lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
}
return type;
}
void Routine(int sockfd, InetAddr addr)
{
funcs["defaultService"](sockfd, addr);
std::string type = Read(sockfd);
lg.LogMessage(Debug, "%s select %s\n", addr.PrintDebug().c_str(), type.c_str());
if (type == "ping")
funcs[type](sockfd, addr);
else if (type == "translate")
funcs[type](sockfd, addr);
else if (type == "transform")
funcs[type](sockfd, addr);
else
{
}
close(sockfd);
}
void DefaultService(const int sockfd, const InetAddr &addr)
{
(void)addr;
std::string service_list = " |";
for (auto func : funcs)
{
service_list += func.first;
service_list += "|";
}
write(sockfd, service_list.c_str(), service_list.size());
}
void Service(int sockfd, InetAddr addr)
{
char buffer[1024];
// 一直进行IO
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
std::cout << addr.PrintDebug() << "# " << buffer << std::endl;
std::string echo_string = "server echo# ";
echo_string += buffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
{
lg.LogMessage(Info, "client quit...\n");
break;
}
else
{
lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
break;
}
}
}
void RegisterFunc(const std::string &name, callback_t func)
{
funcs[name] = func;
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensock;
bool _isrunning;
std::unordered_map<std::string, callback_t> funcs;
};
客户端
这个代码实现了一个简单的TCP客户端,用于连接到一个TCP服务器,获取服务器提供的服务列表,发送用户选择的服务,然后发送和接收消息。下面是对这个客户端类的详细介绍:
头文件和命名空间
代码首先包含了一系列必要的头文件,这些头文件提供了网络编程、字符串处理、输入输出等功能。然后使用了std
命名空间,以简化代码中的标准库类型和函数的调用。
信号处理函数
handler
函数是一个信号处理函数,用于处理接收到的信号。在这个例子中,它仅仅打印出接收到的信号编号,并退出程序。但这个函数在main
函数中被设置为忽略SIGPIPE
信号,这是为了防止在写已关闭的socket时程序异常退出。
使用说明和重试机制
Usage
函数提供了程序的使用说明。如果用户没有提供正确的命令行参数,这个函数会被调用,显示如何使用这个程序。
程序中定义了一个重试计数Retry_Count
,设置为5。这意味着如果连接服务器失败,客户端会尝试重新连接最多5次。
访问服务器的函数
visitServer
函数是客户端的核心功能。它尝试连接到指定的服务器,并交换数据。这个函数的主要步骤如下:
-
创建Socket :使用
socket
函数创建一个TCP socket。 -
连接到服务器 :使用服务器的IP地址和端口号连接到服务器。如果连接失败,会打印错误信息并返回
false
。 -
读取服务列表:从服务器读取提供的服务列表,并显示给用户。
-
用户交互:提示用户选择一个服务,并将选择发送给服务器。然后等待用户输入一条消息,并将其发送给服务器。
-
接收服务器响应 :读取并显示服务器的响应。如果服务器的响应是"quit",则函数返回
true
,表示交互结束。 -
错误处理和资源清理:在函数结束时,关闭socket。
主函数
main
函数是程序的入口点。它首先检查命令行参数的数量,确保用户提供了服务器的IP地址和端口号。然后,它进入一个循环,尝试连接到服务器。如果连接失败,会等待一秒钟后重试,直到达到最大重试次数。如果成功连接到服务器并完成了交互,循环会提前结束。
总结和注意事项
这个客户端程序是一个简单的TCP客户端示例,用于连接到TCP服务器并进行基本的文本交互。它展示了如何使用socket API进行网络通信,包括创建socket、连接到服务器、发送和接收数据等。
需要注意的是,这个程序没有进行复杂的错误处理或安全性检查。在实际应用中,应该添加更多的错误处理和安全性措施,例如检查输入的有效性、使用加密通信等。此外,这个程序使用了阻塞式的socket API,这意味着在读写数据时程序会停止执行直到操作完成。在需要高性能或并发处理的应用中,可能需要使用非阻塞式的socket API或异步I/O库。
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "ErrInfo.hpp"
using namespace std;
void handler(int signo)
{
std::cout << "signo: " << signo << std::endl;
exit(0);
}
#define Retry_Count 5
void Usage(const std::string &process)
{
std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}
bool visitServer(std::string &serverip, uint16_t &serverport, int *cnt)
{
// 1. 创建socket
string inbuffer;
char service_list[1024];
ssize_t m = 0;
ssize_t n = 0;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
cerr << "socket error" << endl;
return false;
}
bool ret = true;
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列
n = connect(sockfd, (sockaddr*)&server, sizeof(server));
if (n < 0)
{
cerr << "connect error" << endl;
ret = false;
goto END;
}
*cnt = 0;
m = read(sockfd, service_list, sizeof(service_list) - 1);
if (m > 0)
{
service_list[m] = 0;
cout << "服务器提供的服务列表是: " << service_list << endl;
}
cout << "请你选择服务# ";
getline(cin, inbuffer);
write(sockfd, inbuffer.c_str(), inbuffer.size());
cout << "Enter> ";
getline(cin, inbuffer);
if (inbuffer == "quit")
return true;
n = write(sockfd, inbuffer.c_str(), inbuffer.size());
if (n > 0)
{
char buffer[1024];
m = read(sockfd, buffer, sizeof(buffer) - 1);
if (m > 0)
{
buffer[m] = 0;
cout << buffer << endl;
}
else if (m == 0)
{
return true;
}
else
{
ret = false;
goto END;
}
}
else
{
std::cout << "hello write Error" << std::endl;
ret = false;
goto END;
}
END:
close(sockfd);
return ret;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
return 1;
}
std::string serverip = argv[1];
uint16_t serverport = stoi(argv[2]);
signal(SIGPIPE, SIG_IGN);
int cnt = 1;
while (cnt <= Retry_Count)
{
bool result = visitServer(serverip, serverport, &cnt);
if (result)
{
break;
}
else
{
sleep(1);
std::cout << "server offline, retrying..., count : " << cnt << std::endl;
cnt++;
}
}
if (cnt >= Retry_Count)
{
std:
cout << "server offline" << std::endl;
}
return 0;
}
所用到的其他类
线程池
#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"
static const int defaultnum = 10;
class ThreadData
{
public:
ThreadData(const std::string &name) : threadname(name)
{
}
~ThreadData()
{
}
public:
std::string threadname;
};
template <class T>
class ThreadPool
{
private:
ThreadPool(int thread_num = defaultnum) : _thread_num(thread_num)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_cond, nullptr);
// 构建指定个数的线程
for (int i = 0; i < _thread_num; i++)
{
// 待优化
std::string threadname = "thread-";
threadname += std::to_string(i + 1);
ThreadData td(threadname);
// Thread<ThreadData> t(threadname,
// std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td);
// _threads.push_back(t);
_threads.emplace_back(threadname,
std::bind(&ThreadPool<T>::ThreadRun, this,
std::placeholders::_1),
td);
lg.LogMessage(Info, "%s is created...\n", threadname.c_str());
}
}
ThreadPool(const ThreadPool<T> &tp) = delete;
const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;
public:
// 有线程安全问题的
static ThreadPool<T> *GetInstance()
{
if (instance == nullptr)
{
LockGuard lockguard(&sig_lock);
if (instance == nullptr)
{
lg.LogMessage(Info, "创建单例成功...\n");
instance = new ThreadPool<T>();
}
}
return instance;
}
bool Start()
{
// 启动
for (auto &thread : _threads)
{
thread.Start();
lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());
}
return true;
}
void ThreadWait(const ThreadData &td)
{
lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());
pthread_cond_wait(&_cond, &_mutex);
}
void ThreadWakeup()
{
pthread_cond_signal(&_cond);
}
void ThreadRun(ThreadData &td)
{
while (true)
{
// checkSelf()
// checkSelf();
// 取任务
T t;
{
LockGuard lockguard(&_mutex);
while (_q.empty())
{
ThreadWait(td);
lg.LogMessage(Debug, "thread %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, t.PrintTask().c_str(), t.PrintResult().c_str());
}
}
void Push(T &in)
{
LockGuard lockguard(&_mutex);
_q.push(in);
ThreadWakeup();
}
~ThreadPool()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
// for debug
void Wait()
{
for (auto &thread : _threads)
{
thread.Join();
}
}
private:
std::queue<T> _q;
std::vector<Thread<ThreadData>> _threads;
int _thread_num;
pthread_mutex_t _mutex;
pthread_cond_t _cond;
static ThreadPool<T> *instance;
static pthread_mutex_t sig_lock;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;
线程类
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;
template<class T>
class Thread
{
public:
Thread(const std::string &threadname, func_t<T> func, T &data)
:_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data)
{}
static void *ThreadRoutine(void *args) // 类内方法,
{
// (void)args; // 仅仅是为了防止编译器有告警
Thread *ts = static_cast<Thread *>(args);
ts->_func(ts->_data);
return nullptr;
}
bool Start()
{
int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);
if(n == 0)
{
_isrunning = true;
return true;
}
else return false;
}
bool Join()
{
if(!_isrunning) return true;
int n = pthread_join(_tid, nullptr);
if(n == 0)
{
_isrunning = false;
return true;
}
return false;
}
std::string ThreadName()
{
return _threadname;
}
bool IsRunning()
{
return _isrunning;
}
~Thread()
{}
private:
pthread_t _tid;
std::string _threadname;
bool _isrunning;
func_t<T> _func;
T _data;
};
翻译业务类
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"
const std::string unknown = "unknown";
const std::string mydict = "./recource/dict.txt";
const std::string sep = " ";
class Translate
{
public:
Translate(std::string dict_path = mydict) : _dict_path(dict_path)
{
LoadDict();
Parse();
}
void LoadDict()
{
std::ifstream in(_dict_path);
std::string line;
while(std::getline(in, line))
{
lines.push_back(line);
}
in.close();
lg.LogMessage(Debug, "Load dict txt success, path: %s\n", _dict_path.c_str());
}
void Parse()
{
for(auto &line : lines)
{
auto pos = line.find(sep);
if(pos == std::string::npos) continue;
else
{
std::string word = line.substr(0, pos);
std::string chinese = line.substr(pos+sep.size());
_dict.insert(std::make_pair(word, chinese));
}
}
lg.LogMessage(Debug, "Parse dict txt success, path: %s\n", _dict_path.c_str());
}
void debug()
{
// for(auto &line : lines)std::cout << line << std::endl;
for(auto &elem : _dict)
{
std::cout << elem.first << " : " << elem.second << std::endl;
}
}
std::string Excute(const std::string &word)
{
auto iter = _dict.find(word);
if (iter == _dict.end())
return unknown;
else
return _dict[word];
}
~Translate()
{
}
private:
std::string _dict_path;
std::unordered_map<std::string, std::string> _dict;
std::vector<std::string> lines;
};
禁止拷贝类
#pragma once
#include<iostream>
class NoCopy
{
public:
NoCopy()
{}
NoCopy(const NoCopy&) = delete;
NoCopy& operator=(const NoCopy&) = delete;
~NoCopy()
{}
};
守护锁类
#pragma once
#include <pthread.h>
// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:
Mutex(pthread_mutex_t *lock):_lock(lock)
{}
void Lock()
{
pthread_mutex_lock(_lock);
}
void Unlock()
{
pthread_mutex_unlock(_lock);
}
~Mutex()
{}
private:
pthread_mutex_t *_lock;
};
class LockGuard
{
public:
LockGuard(pthread_mutex_t *lock): _mutex(lock)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex _mutex;
};
网络地址转换类
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
class InetAddr
{
public:
InetAddr(struct sockaddr_in &addr):_addr(addr)
{
_port = ntohs(_addr.sin_port);
// _ip = inet_ntoa(_addr.sin_addr); //char *inet_ntoa(struct in_addr in),返回的是字符串地址,那么字符串在哪里?
char ipbuffer[64];
inet_ntop(AF_INET, &addr.sin_addr, ipbuffer, sizeof(ipbuffer)); // 1. 网络转本机 2. 4字节ip,字符串风格的ip
_ip = ipbuffer;
}
std::string Ip() {return _ip;}
uint16_t Port() {return _port;};
std::string PrintDebug()
{
std::string info = _ip;
info += ":";
info += std::to_string(_port); // "127.0.0.1:4444"
return info;
}
const struct sockaddr_in& GetAddr()
{
return _addr;
}
bool operator == (const InetAddr&addr)
{
//other code
return this->_ip == addr._ip && this->_port == addr._port;
}
~InetAddr(){}
private:
std::string _ip;
uint16_t _port;
struct sockaddr_in _addr;
};
日志类
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
enum
{
Debug = 0,
Info,
Warning,
Error,
Fatal
};
enum
{
Screen = 10,
OneFile,
ClassFile
};
std::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 "Unknown";
}
}
const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";
class Log
{
public:
Log() : style(defaultstyle), filename(default_filename)
{
mkdir(logdir.c_str(), 0775);
}
void Enable(int sty) //
{
style = sty;
}
std::string TimeStampExLocalTime()
{
time_t currtime = time(nullptr);
struct tm *curr = localtime(&currtime);
char time_buffer[128];
snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
curr->tm_hour, curr->tm_min, curr->tm_sec);
return time_buffer;
}
void WriteLogToOneFile(const std::string &logname, const std::string &message)
{
umask(0);
int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
if (fd < 0)
return;
write(fd, message.c_str(), message.size());
close(fd);
}
void WriteLogToClassFile(const std::string &levelstr, const std::string &message)
{
std::string logname = logdir;
logname += "/";
logname += filename;
logname += levelstr;
WriteLogToOneFile(logname, message);
}
void WriteLog(const std::string &levelstr, const std::string &message)
{
switch (style)
{
case Screen:
std::cout << message;
break;
case OneFile:
WriteLogToClassFile("all", message);
break;
case ClassFile:
WriteLogToClassFile(levelstr, message);
break;
default:
break;
}
}
void LogMessage(int level, const char *format, ...) // 类C的一个日志接口
{
char leftbuffer[1024];
std::string levelstr = LevelToString(level);
std::string currtime = TimeStampExLocalTime();
std::string idstr = std::to_string(getpid());
char rightbuffer[1024];
va_list args; // char *, void *
va_start(args, format);
// args 指向了可变参数部分
vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
va_end(args); // args = nullptr;
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
levelstr.c_str(), currtime.c_str(), idstr.c_str());
std::string loginfo = leftbuffer;
loginfo += rightbuffer;
WriteLog(levelstr, loginfo);
}
~Log() {}
private:
int style;
std::string filename;
};
Log lg;
class Conf
{
public:
Conf()
{
lg.Enable(ClassFile);
}
~Conf()
{
}
};
Conf conf;
守护进程类
#pragma once
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
const char *root = "/";
const char *dev_null = "/dev/null";
void Daemon(bool ischdir, bool isclose)
{
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
if (fork() > 0)
exit(0);
setsid();
if (ischdir)
chdir(root);
if (isclose)
{
close(0);
close(1);
close(2);
}
else
{
int fd = open(dev_null, O_RDWR);
if (fd > 0)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
}
}