【Linux网络编程六】服务器守护进程化Daemon

【Linux网络编程六】服务器守护进程化Daemon

  • 一.背景知识:前台与后台
  • 二.相关操作
  • 三.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.h>

const 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 fd=open(nullfile.c_str(),O_RDWR);
    if(fd>0)//打开成功
    {
       
       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 defaultip="0.0.0.0";
const int defaultfd=-1;
int backlog=10;//一般不要设置太大
enum 
{
    SockError=2,
    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 &ip=defaultip):_listensock(-1),_port(port),_ip(ip)
     {}
    
     void Init()
     {
        //服务器端启动之前创建套接字,绑定。
        //一开始的这个套接字是属于监听套接字
        _listensock=socket(AF_INET,SOCK_STREAM,0);
       if(_listensock<0)
       {
         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_family=AF_INET;
       local.sin_port=htons(_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* td=static_cast<ThreadData*>(args);
      //子线程用来服务客户端
      td->_svr->Service(td->_sockfd,td->_ip,td->_port);
      delete td;
      return nullptr;
     }
     void Run()
     {
      Daemon();
      signal(SIGPIPE,SIG_IGN);//防止服务端往一个已经关闭的文件描述符里写入,忽略带操作系统发送的信号
      //一启动服务器,就将线程池中的线程创建
      ThreadPool<TASK>::GetInstance()->Start();//单例对象
      //静态函数,通过类域就可以使用
    
      lg(Info,"tcpserver is running");
       while(true)
       {
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        //将套接字设置成监听状态后,就可以获取新连接
        int sockfd=accept(_listensock,(struct sockaddr*)&client,&len);
        //获取从监听套接字那里监听到的连接。然后返回一个新套接字,通过这个套接字与连接直接通信,而监听套接字继续去监听。
        if(sockfd<0)
        {
          lg(Fatal,"accept error,errno: %d, errstring: %s",errno,strerror(errno));
          exit(AcceptError);
        }
        //获取新连接成功
        //将客户端端网络信息带出来
        uint16_t clientport=ntohs(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); 
       //将任务放进线程池里,线程就会到线程池里去执行任务。
       ThreadPool<TASK>::GetInstance()->Push(t);

       }
      
     }
  
     void Service(int &sockfd,const std::string &clientip,uint16_t &clientport)
     {
         char inbuffer[1024];
         while(true)
         {
          ssize_t n=read(sockfd,inbuffer,sizeof(inbuffer));
          if(n>0)
          {
            inbuffer[n]=0;
            std::cout<<"client say# "<<inbuffer<<std::endl;
            //加工处理一下
            std::string echo_string="tcpserver加工处理数据:";
            echo_string+=inbuffer;

            //将加工处理的数据发送会去
            write(sockfd,echo_string.c_str(),echo_string.size());
          }
          else if(n==0)//如果没有用户连接了,那么就会读到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.h>


void Usage(std::string proc)
{
    std::cout<<"\n\rUsage: "<<proc<<" port[1024+]\n"<<std::endl;
}
//./tcpclient ip port
int main(int args,char* argv[])
{
    if(args!=3)
    {
     Usage(argv[0]);
     exit(1);
    }
    std::string serverip=argv[1];
    uint16_t serverport = std::stoi(argv[2]);
    struct sockaddr_in server;
    socklen_t len=sizeof(server);
    server.sin_family=AF_INET;
    server.sin_port=htons(serverport);
    inet_pton(AF_INET,serverip.c_str(),&server.sin_addr);

    while(true)
    {
    //创建套接字
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        std::cout<<"create sockfd err "<<std::endl;
    }
    //创建套接字成功,创建完套接字后该干什么?
    //连接服务器端的套接字,所以客户端用户需要知道服务器端的网络信息的
    int cnt=10;
    bool isreconnect=false;
    do
    { 
       int n=connect(sockfd,(struct sockaddr*)&server,len);
      if(n<0)//服务器关闭了,肯定会连接失败
      {
        isreconnect=true;
        cnt--;
        std::cout<<"connect sock err...,cnt: "<<cnt<<std::endl;
        sleep(12);
      }
      else//重连成功了
      {
        break;
      }
    }while(cnt&&isreconnect);
    //连接成功
    //连接成功后,就可以直接通信了,就可以直接给对方写消息了。
    if(cnt==0)
    {
        std::cerr<<"user offline.."<<std::endl;
        break;//用户直接不玩了
    }
    std::string message;
   
    std::cout<<"Please enter#";
    getline(std::cin,message);
    //往套接字里写
    int n=write(sockfd,message.c_str(),message.size());
    if(n<0)//服务器端会将该套接字关闭,然后就写不进去了。需要重新创建套接字连接
    {
        std::cerr<<"write error..."<<std::endl;
        continue;
    }
    char outbuffer[1024];
    //接收服务器发送的加工处理消息

    n=read(sockfd,outbuffer,sizeof(outbuffer));
    if(n>0)
    {
       outbuffer[n]=0;
       std::cout<<outbuffer<<std::endl;
    }
    close(sockfd);
    }
    return 0;
 
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/390486.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最小生成树(Kruskal算法及相关例题)

1.Kruskal算法概念以及基本思路 &#xff08;1&#xff09;概念&#xff1a; 克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。它的时间复杂度为O&#xff08;ElogE&#xff09;(E是图G的边的总数)&#xff0c;适合于求边稀疏的网的最小生成树 。 其基本思想是&#xff…

JDBC访问数据库

目录 加载Driver驱动配置驱动地址 获取数据库连接创建会话-SQL命令发送器通过Statement发送SQL命令并得到结果处理结果关闭数据库资源测试 加载Driver驱动 1.在模块JDBC中新建一个lib目录文件 2. 将mysql-connector-j-8.2.0包粘贴至lib目录中。 配置驱动地址 // 加载…

Nvidia 携手 RTX 推出的本地运行 AI 聊天机器人

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

人工智能学习与实训笔记(三):神经网络之目标检测问题

目录 五、目标检测问题 5.1 目标检测基础概念 5.1.1 边界框&#xff08;bounding box&#xff09; 5.1.2 锚框&#xff08;Anchor box&#xff09; 5.1.3 交并比 5.2 单阶段目标检测模型YOLOv3 5.2.1 YOLOv3模型设计思想 5.2.2 YOLOv3模型训练过程 5.2.3 如何建立输出…

uni-app 经验分享,从入门到离职(二)—— tabBar 底部导航栏实战基础篇

文章目录 &#x1f4cb;前言⏬关于专栏 &#x1f3af;关于小程序 tabbar 的一些知识&#x1f3af;创建一个基本的 tabBar&#x1f4dd;最后 &#x1f4cb;前言 这篇文章的内容主题是关于小程序的 tabBar 底部导航栏的入门使用和实战技巧。通过上一篇文章的基础&#xff0c;我们…

【sgCreateTableColumn】自定义小工具:敏捷开发→自动化生成表格列html代码(表格列生成工具)[基于el-table-column]

源码 <template><!-- 前往https://blog.csdn.net/qq_37860634/article/details/136126479 查看使用说明 --><div :class"$options.name"><div class"sg-head">表格列生成工具</div><div class"sg-container"…

C++,stl,常用排序算法,常用拷贝和替换算法

目录 1.常用排序算法 sort random_shuffle merge reverse 2.常用拷贝和替换算法 copy replace replace_if swap 1.常用排序算法 sort 默认从小到大排序 #include<bits/stdc.h> using namespace std;int main() {vector<int> v;v.push_back(1);v.push_ba…

RabbitMQ如何保证可靠

0. RabbitMQ不可靠原因 消息从生产者到消费者的每一步都可能导致消息丢失&#xff1a; 发送消息时丢失&#xff1a; 生产者发送消息时连接MQ失败生产者发送消息到达MQ后未找到Exchange生产者发送消息到达MQ的Exchange后&#xff0c;未找到合适的Queue消息到达MQ后&#xff0c;…

idea里微服务依赖在maven能install但不能启动

场景&#xff1a;多个微服务相互依赖&#xff0c;install都没问题&#xff0c;jar包都是正常的&#xff0c;就连jar都能启动&#xff0c;为什么在idea里面项目就是不能启动呢&#xff0c;我是懵逼的 所以解决办法就是&#xff1a; 在设置的编译器里面虚拟机选项添加 -Djps.tr…

第五节 zookeeper集群与分布式锁_2

1.分布式锁概述 1.1 什么是分布式锁 1&#xff09;要介绍分布式锁&#xff0c;首先要提到与分布式锁相对应的是线程锁。 线程锁&#xff1a;主要用来给方法、代码块加锁。当某个方法或代码使用锁&#xff0c;在同一时刻仅有一个线程执行该方法或该代码段。 线程锁只在同一J…

LEETCODE 164. 破解闯关密码

class Solution { public:string crackPassword(vector<int>& password) {vector<string> password_str;for(int i0;i<password.size();i){password_str.push_back(to_string(password[i]));}//希尔排序int gappassword.size()/2;while(gap>0){for(int i…

命令执行讲解和函数

命令执行漏洞简介 命令执行漏洞产生原因 应用未对用户输入做严格得检查过滤&#xff0c;导致用户输入得参数被当成命令来执行 命令执行漏洞的危害 1.继承Web服务程序的权限去执行系统命会或读写文件 2.反弹shell&#xff0c;获得目标服务器的权限 3.进一步内网渗透 远程代…

python----输入输出算数运算

1.格式化输出 如果我们直接打印输出&#xff0c;就是输出变量的值&#xff0c;例如&#xff1a; 如果我们想打印a10就需要格式化字符串&#xff0c;就是使用f进行格式化&#xff0c;如图所示&#xff1b; 2.控制台输入 input执行的时候&#xff0c;就会等待用户进行输入&…

Qlik Sense : 条形图

条形图 “条形图适合比较多个值。维度轴显示所比较的类别条目&#xff0c;度量轴显示每个类别条目的值。” Qlik Sense中的条形图是一种数据可视化工具&#xff0c;用于展示不同类别或维度之间的比较。它通过水平或垂直的条形表示数据&#xff0c;并根据数值的大小进行排序。…

RK3568平台开发系列讲解(存储篇)文件描述符相关系统调用实现

🚀返回专栏总目录 文章目录 一、open 系统调用二、close 系统调用沉淀、分享、成长,让自己和他人都能有所收获!😄 一、open 系统调用 open()系统调用会分配新的文件句柄(file description),用来维护与打开文件相关的元信息(如偏移量、路径、操作方法等),并会给进程…

微信小程序框架阐述

目录 一、框架 响应的数据绑定 页面管理 基础组件 丰富的 API 二、逻辑层 App Service 小程序的生命周期 注册页面 使用 Page 构造器注册页面 在页面中使用 behaviors 使用 Component 构造器构造页面 页面的生命周期 页面路由 页面栈 路由方式 注意事项 模块化…

Git 初学

目录 一、需求的产生 二、版本控制系统理解 1. 认识版本控制系统 2. 版本控制系统分类 &#xff08;1&#xff09;集中式版本控制系统 缺点&#xff1a; &#xff08;2&#xff09;分布式版本控制系统 三、初识 git 四、git 的使用 例&#xff1a;将 “ OLED文件夹 ”…

单部10层电梯控制系列之UDT数据类型的建立(SCL代码)

这篇博客开始介绍单部10层电梯的完整控制程序编写过程&#xff0c;编程语言&#xff1a;SCL&#xff0c;控制器型号&#xff1a;S7-1200PLC。开篇博客我们介绍电梯控制用到的所有UDT数据类型。在学习本篇博客之前大家可以参考下面文章&#xff0c;了解博途PLC里的UDT数据类型是…

紫微斗数双星组合:天机太阴在寅申

文章目录 前言内容总结 前言 紫微斗数双星组合&#xff1a;天机太阴在寅申 内容 紫微斗数双星组合&#xff1a;天机太阴在寅申 性格分析 天机星与太阴星同坐寅申二宫守命的男性&#xff0c;多浪漫&#xff0c;易与女性接近&#xff0c;温柔体贴&#xff0c;懂得女人的心理。…

.NET Core WebAPI中使用Log4net记录日志

一、安装NuGet包 二、添加配置 // log4net日志builder.Logging.AddLog4Net("CfgFile/log4net.config");三、配置log4net.config文件 <?xml version"1.0" encoding"utf-8"?> <log4net><!-- Define some output appenders -->…