网站开发的重难点,丁香园做科室网站,软件开发外包多少钱,织梦网站开发语言【Linux网络编程六】服务器守护进程化Daemon 一.背景知识#xff1a;前台与后台二.相关操作三.Linux的进程间关系四.自成会话五.守护进程四步骤六.服务器守护进程化 一.背景知识#xff1a;前台与后台 核心知识就是一个用户在启动Linux时#xff0c;都会给一个session会话前台与后台二.相关操作三.Linux的进程间关系四.自成会话五.守护进程四步骤六.服务器守护进程化 一.背景知识前台与后台 核心知识就是一个用户在启动Linux时都会给一个session会话这个会话里会存在一个前台进程和多个后台进程。 二.相关操作 三.Linux的进程间关系
Linux中的进程之间的关系不仅仅是相互独立的还可以过程进程组进程组的组长就是第一个进程的pid。
四.自成会话 当一个任务以后台进程在会话中执行然后我们将会话关闭重新启动一个会话将会发现这个任务虽然还存在但其实已经不再是原先的那个任务了因为它的父进程bash已经退出它被系统自动领养了。(它原来的会话是会被记录下来的)
所以进程组是会收到用户的登录和退出的影响。进程组就代表着一个任务也就是任务是会收到用户的退出影响用户一旦退出那么该任务就不再属于你了也就是该任务已经没有了。你把会话都关闭了那么里面的所有任务都会不存在了。
这里我想说的就是我们想让一个任务一直执行不受用户的登录和退出影响就必须使用守护进程 守护进程的核心就是自成会话。 因为创建新会话的进程不能是进程组里的组长所以我们就直接让当前进程创建子进程然后再让当前进程直接退出让子进程创建会话。
五.守护进程四步骤
守护进程四步骤①忽略其他信号②自成会话③更改工作目录④重定向标准输入与输出。 因为自成会话后新会话就不再与终端有联系了就不需要标准输出和标准输入和标准错误了。 而打印日志可以直接往文件里输入。
其实系统中提供了守护进程化的接口调用
六.服务器守护进程化
其实守护进程的本质就是后台进程服务器一旦启动了守护进程化那么就不会受用户的退出影响就会一直在运行。
//守护进程
#pragma once#include signal.h
#include unistd.h
#include string
#include sys/types.h
#include sys/stat.h
#include fcntl.hconst std::string nullfile/dev/null;
void Daemon(const std::string cwd)
{//第一步忽略其他异常信号防止被信号杀死signal(SIGCLD,SIG_IGN);signal(SIGPIPE,SIG_IGN);signal(SIGSTOP,SIG_IGN);//第二步将自己变成新的会话不受其他会话管理if(fork()0)exit(0);//让孙子进程来创建新的会话因为自成组长的进程不能创建新会话setsid();//让使用该函数的进程创建新的会话并属于该会话//第三步更改当前进程的路径//根据需要传入不同的路径就更改到不同路径下if(!cwd.empty()){chdir(cwd.c_str());}//第四步将标准输入标准输出标志错误重定向到垃圾桶文件里新的会话不再与终端关联int fdopen(nullfile.c_str(),O_RDWR);if(fd0)//打开成功{dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);}}#pragma once#include iostream
#include string
#include cstring
#include cstdlib
#include sys/wait.h
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.h
#include unistd.h
#include pthread.h
#include signal.h
#include Log.hpp
#include TASK.hpp
#include ThreadPool.hpp
#include Daemon.hpp
Log lg;const std::string defaultip0.0.0.0;
const int defaultfd-1;
int backlog10;//一般不要设置太大
enum
{SockError2,BindError,AcceptError,
};
class Tcpserver;class ThreadData
{
public:ThreadData(int fd,const std::string ip,uint16_t port,Tcpserver* svr):_sockfd(fd),_ip(ip),_port(port),_svr(svr){}
public: int _sockfd;std::string _ip;uint16_t _port;Tcpserver* _svr;
};class Tcpserver
{
public:Tcpserver(const uint16_t port,const std::string ipdefaultip):_listensock(-1),_port(port),_ip(ip){}void Init(){//服务器端启动之前创建套接字绑定。//一开始的这个套接字是属于监听套接字_listensocksocket(AF_INET,SOCK_STREAM,0);if(_listensock0){lg(Fatal,sock create errno:%d errstring:%s,errno,strerror(errno));exit(SockError);}//创建套接字成功lg(Info,sock create sucess listensock:%d,_listensock);int opt 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)//创建成功后就要绑定服务器的网络信息struct sockaddr_in local;memset(local,0,sizeof(local));//填充信息local.sin_familyAF_INET;local.sin_porthtons(_port);inet_aton(_ip.c_str(),local.sin_addr);//填充完毕真正绑定if((bind(_listensock,(struct sockaddr*)local,sizeof(local)))0){lg(Fatal,bind errno:%d errstring:%s,errno,strerror(errno));exit(BindError);}lg(Info,bind socket success listensock:%d,_listensock);//绑定成功//udp中绑定成功后就可以进行通信了但tcp与udp不同。tcp是面向连接的在通信之前//需要先获取新连接获取到新连接才能进行通信。没有获取连接那么就要等待连接等待新连接的过程叫做监听监听有没有新连接。//需要将套接字设置成监听状态listen(_listensock,backlog);//用来监听等待新连接只有具备监听状态才能识别到连}static void* Routine(void *args)//静态成员函数无法使用成员函数再封装一个服务器对象{//子线程要和主线程分离主线程不需要等待子线程直接回去重新获取新连接pthread_detach(pthread_self());ThreadData* tdstatic_castThreadData*(args);//子线程用来服务客户端td-_svr-Service(td-_sockfd,td-_ip,td-_port);delete td;return nullptr;}void Run(){Daemon();signal(SIGPIPE,SIG_IGN);//防止服务端往一个已经关闭的文件描述符里写入忽略带操作系统发送的信号//一启动服务器就将线程池中的线程创建ThreadPoolTASK::GetInstance()-Start();//单例对象//静态函数通过类域就可以使用lg(Info,tcpserver is running);while(true){struct sockaddr_in client;socklen_t lensizeof(client);//将套接字设置成监听状态后就可以获取新连接int sockfdaccept(_listensock,(struct sockaddr*)client,len);//获取从监听套接字那里监听到的连接。然后返回一个新套接字通过这个套接字与连接直接通信而监听套接字继续去监听。if(sockfd0){lg(Fatal,accept error,errno: %d, errstring: %s,errno,strerror(errno));exit(AcceptError);}//获取新连接成功//将客户端端网络信息带出来uint16_t clientportntohs(client.sin_port);char clientip[32];inet_ntop(AF_INET,client.sin_addr,clientip,sizeof(clientip));//根据新连接进行通信lg(Info,get a new link...sockfd: %d,clientip: %s,clientport: %d,sockfd,clientip,clientport);//构建任务TASK t(sockfd,clientip,clientport); //将任务放进线程池里,线程就会到线程池里去执行任务。ThreadPoolTASK::GetInstance()-Push(t);}}void Service(int sockfd,const std::string clientip,uint16_t clientport){char inbuffer[1024];while(true){ssize_t nread(sockfd,inbuffer,sizeof(inbuffer));if(n0){inbuffer[n]0;std::coutclient say# inbufferstd::endl;//加工处理一下std::string echo_stringtcpserver加工处理数据;echo_stringinbuffer;//将加工处理的数据发送会去write(sockfd,echo_string.c_str(),echo_string.size());}else if(n0)//如果没有用户连接了那么就会读到0.服务器端也就不要再读了{lg(Info,%s:%d quit, server close sockfd: %d,clientip.c_str(),clientport,sockfd);break;}else{lg(Fatal,read errno: %d, errstring: %s,errno,strerror(errno));}}}~Tcpserver(){}private:int _listensock;//监听套接字只有一个监听套接字用来不断获取新的连接。返回新的套接字std::string _ip;uint16_t _port;
};#include iostream
#include cstdlib
#include unistd.h
#include strings.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.hvoid Usage(std::string proc)
{std::cout\n\rUsage: proc port[1024]\nstd::endl;
}
//./tcpclient ip port
int main(int args,char* argv[])
{if(args!3){Usage(argv[0]);exit(1);}std::string serveripargv[1];uint16_t serverport std::stoi(argv[2]);struct sockaddr_in server;socklen_t lensizeof(server);server.sin_familyAF_INET;server.sin_porthtons(serverport);inet_pton(AF_INET,serverip.c_str(),server.sin_addr);while(true){//创建套接字int sockfdsocket(AF_INET,SOCK_STREAM,0);if(sockfd0){std::coutcreate sockfd err std::endl;}//创建套接字成功创建完套接字后该干什么?//连接服务器端的套接字所以客户端用户需要知道服务器端的网络信息的int cnt10;bool isreconnectfalse;do{ int nconnect(sockfd,(struct sockaddr*)server,len);if(n0)//服务器关闭了肯定会连接失败{isreconnecttrue;cnt--;std::coutconnect sock err...,cnt: cntstd::endl;sleep(12);}else//重连成功了{break;}}while(cntisreconnect);//连接成功//连接成功后就可以直接通信了就可以直接给对方写消息了。if(cnt0){std::cerruser offline..std::endl;break;//用户直接不玩了}std::string message;std::coutPlease enter#;getline(std::cin,message);//往套接字里写int nwrite(sockfd,message.c_str(),message.size());if(n0)//服务器端会将该套接字关闭然后就写不进去了。需要重新创建套接字连接{std::cerrwrite error...std::endl;continue;}char outbuffer[1024];//接收服务器发送的加工处理消息nread(sockfd,outbuffer,sizeof(outbuffer));if(n0){outbuffer[n]0;std::coutoutbufferstd::endl;}close(sockfd);}return 0;}