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

news2024/11/24 15:55:03

【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;
 
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Springboot的社区物资交易互助平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区物资交易互助平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

leetcode刷题(罗马数字转数字)

1.题目描述 2.解题思路 这时候已经给出了字母对应的数字&#xff0c;我们只需要声明一个字典&#xff0c;将罗马数字和数字之间的对应关系声明即可。其中可能涉及到会出现两个连续的罗马字母代表一个数字&#xff0c;这时候我们需要判断遍历的字符和将要遍历的下一个字符是否存…

pytorch 实现线性回归(深度学习)

一 查看原始函数 初始化 %matplotlib inline import random import torch from d2l import torch as d2l 1.1 生成原始数据 def synthetic_data(w, b, num_examples):x torch.normal(0, 1, (num_examples, len(w)))y torch.matmul(x, w) bprint(x:, x)print(y:, y)y tor…

Mysql第二关之存储引擎

简介 所有关于Mysql数据库优化的介绍仿佛都有存储引擎的身影。本文介绍Mysql常用的有MyISAM存储引擎和Innodb存储引擎&#xff0c;还有常见的索引。 Mysql有两种常见的存储引擎&#xff0c;MyISAM和Innodb&#xff0c;它们各有优劣&#xff0c;经过多次优化和迭代&#xff0c;…

【STM32 CubeMX】SPI HAL库编程

文章目录 前言一、CubeMX配置SPI Flash二、SPI HAL编程2.1 查询方式函数2.2 使用中断方式2.3 DMA方式 总结 前言 STM32 CubeMX 是一款由 STMicroelectronics 提供的图形化配置工具&#xff0c;用于生成 STM32 微控制器的初始化代码和项目框架。在 STM32 开发中&#xff0c;使用…

JDBC查询操作

目录 加载驱动获取连接创建会话发送SQL处理结果关闭资源测试 加载驱动 // 加载驱动Class.forName("com.mysql.cj.jdbc.Driver");获取连接 // 获取连接String url "jdbc:mysql://127.0.0.1:3306/book";String username "root" …

2024全新领域,适合新手发展的渠道,年后不愁资金问题!

我是电商珠珠 如今年已经过完了&#xff0c;不少人还在迷茫自己开工后要做些什么&#xff0c;部分人还在想着去做一些不用吃力就能赚钱的工作&#xff0c;或是一份能兼顾自己日常生活的兼职。 其实&#xff0c;任何赚钱的工作要么动脑要么费力。 费力的工作有很多&#xff0…

敦煌网怎么提升流量的?如何进行自养号测评提升转化率?

敦煌网作为中国领先的跨境电商平台&#xff0c;对于商家而言&#xff0c;提升流量是增加曝光和销售的重要手段。以下将介绍敦煌网提升流量的几种方法。 一、敦煌网怎么提升流量的? 首先&#xff0c;通过合理的商品定位和市场调研&#xff0c;选择有潜力和竞争优势的商品进行…

android获取sha1

1.cmd在控制台获取 切换到Android Studio\jre\bin目录下执行keytool -list -v -keystore 签名文件路径例如&#xff1a; 2.也可以在android studio中获取 在Terminal中输入命令&#xff1a;keytool -list -v -keystore 签名文件路径获取 获取到的sha1如下&#xff1a;

linux系统zabbix工具监控web页面

web页面监控 内建key介绍浏览器配置浏览器页面查看方式 监控指定的站点的资源下载速度&#xff0c;及页面响应时间&#xff0c;还有响应代码&#xff1b; web Scenario&#xff1a; web场景&#xff08;站点&#xff09;web page &#xff1a;web页面&#xff0c;一个场景有多…

【LeetCode: 103. 二叉树的锯齿形层序遍历 + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

OpenAl 视频生成模型 —— Sora技术报告解读

这里是陌小北&#xff0c;一个正在研究硅基生命的碳基生命。正在努力成为写代码的里面背诗最多的&#xff0c;背诗的里面最会写段子的&#xff0c;写段子的里面代码写得最好的…厨子。 写在前面 早上醒来&#xff0c;就看到OpenAl推出的视频模型Sora炸锅了&#xff0c;感觉所…

WordPress站点成功升级后的介绍页地址是什么?

我们一般在WordPress站点后台 >> 仪表盘 >> 更新中成功升级WordPress的话&#xff0c;最后打开的就是升级之后的版本介绍页。比如boke112百科前两天升级到WordPress 6.4.2后显示的介绍页如下图所示&#xff1a; 该介绍除了介绍当前版本修复了多少个问题及修补了多少…

BUUCTF第十九、二十题解题思路

目录 第十九题rome 第二十题rsa 第十九题rome 解压、查壳。 无壳&#xff0c;用32位IDA打开&#xff0c;检索字符串&#xff0c;找到一个字符串“You are correct!”&#xff0c;与flag相关&#xff0c;对其交叉引用找到函数&#xff0c;查看伪代码。 int func() {int resul…

二叉树前序中序后序遍历(非递归)

大家好&#xff0c;又和大家见面啦&#xff01;今天我们一起去看一下二叉树的前序中序后序的遍历&#xff0c;相信这个对大家来说是信手拈来&#xff0c;但是&#xff0c;今天我们并不是使用常见的递归方式来解题&#xff0c;我们采用迭代方式解答。我们先看第一道前序遍历 1…

LabVIEW开发DUP实时监控系统

LabVIEW开发DUP实时监控系统 该项目采用虚拟仪器设计理念&#xff0c;以LabVIEW作为核心技术平台&#xff0c;开发了一套磁控溅射过程的实时监控系统。实现过程中关键参数的全面数据采集与处理&#xff0c;建立完整的历史数据库&#xff0c;以支持涂层技术的改进和系统向模糊控…

英文论文(sci)解读复现【NO.20】TPH-YOLOv5++:增强捕获无人机的目标检测跨层不对称变压器的场景

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

输入捕获模式PWM输入模式(PWMI)

一、概念介绍 输出比较&#xff1a; 比较电路输入的CNT、CCR大小关系 &#xff0c;在通道引脚输出高低电平 二、频率知识、测量方法补充 N/fc得到标准频率的时长&#xff0c;也就是待测频率的周期 测频法代码实现&#xff1a;修改对射式红外传感器计次&#xff08;上升沿计…

【Linux】管道文件 打包压缩 文本编辑器nano 进度条

目录 什么是管道文件&#xff1f; 打包和压缩 文本编辑器 nano的安装 nano的使用 退出nano编辑&#xff0c;ctrlx 普通用户无法sudo&#xff0c;该怎么解决 Linux小程序-进度条 预备知识 1.回车换行 2.缓冲区 准备工作 代码实现 1.processBar.h代码编写 2.main.c代…

优秀的电机驱动MCU:MM32SPIN360C

DC-DC电源布局注意点&#xff1a; 电源模块布局布线可提前下载芯片的datasheet&#xff08;数据表&#xff09;&#xff0c;按照推荐的布局和布线进行设计。 1) 芯片电源接近原则&#xff1a; 对于为芯片提供电压的开关电源&#xff0c;应确保它尽量靠近芯片放置。这样可以避…