基于tcp协议的网络通信(将服务端守护进程化)

目录

守护进程化

引入

介绍

如何实现 

思路

接口 -- setsid

注意点 

实现代码

daemon.hpp

log.hpp

运行情况


前情提要 -- 前后台任务介绍(区别+命令),session+sid介绍,session退出后的情况(nuhup,终端进程控制组),任务+进程组概念,任务与进程组的关系,-bash介绍-CSDN博客

守护进程化

引入

如果让他们可以不受这些的影响,这就是守护进程化

介绍

是一种使进程脱离终端会话控制,并在后台持续运行的技术

  • 守护进程通常是作为服务在系统中运行的后台进程,它们不会受到终端会话退出的影响,并且可以在系统启动时自动启动

如何实现 

思路

我们已经知道,启动的任务会自动属于当前的会话 -> 那么就会随着会话的退出而受到影响,且这个影响未知(有可能被终止,有可能还存在)

  • 如果我们想要让启动的任务不受到会话的影响,就可以让他自成一个会话
  • 也就是我们自己创建一个新会话,把它迁移过去
  • 这样,这个任务就不会受到原来那个会话的影响了,因为它已经归属其他会话了
  • (当然,这个新的会话不需要与键盘文件交互)

接口 -- setsid

创建一个会话,让当前进程脱离当前会话,成为新会话的进程组,并且让调用这个函数的进程的pgid作为sid

但他有一个条件,进程组的组长不能调用这个函数

  • 就相当于 -- 你在公司如果是个组长的职位,你告诉老板说自己想出去单干,老板肯定不愿意,他会说,你走不行,你下面的组员走可以
  • 所以,结合任务中第一个进程作为组长来看,我们可以考虑借助父子进程的特性来实现
  • 父进程会是组长,让他退出
  • 子进程作为组员,去执行守护进程化的工作
  • (毕竟父进程干等着也没啥用,工作是子进程的,唯一需要的就是释放子进程的资源,但这一工作可以让init进程干,也就是托孤给它)

注意点 

如果我们将服务端变成守护进程,那我们是不需要它在显示器上打印

  • 所以我们可以考虑将标准输出/错误关闭
  • 标准输入也不需要,因为后台任务本身就无法和键盘交互

但我们的服务端之前写过两种需要的打印:日志和debug语句

  • 如果直接关闭文件描述符,会导致写入错误
  • 所以,可以将他们重定向

如果需要日志的话,可以将它重定向到log.txt文件里

但dug语句确实不需要了,其中有两种做法:

  • 删除相关代码(但在代码量过大时,就不太方便了)
  • 将标准流重定向到/dev/null (它会将收到的数据直接丢弃,相当于垃圾桶)

当然,不要忘记处理SIGCHLD和SIGHUP这两个信号的处理方式

  • 因为我们的代码中可能会涉及到这两个信号:父子进程,终端退出等
  • 其他信号根据自己来设定,如果不希望这个服务端被暂停,也可以忽略SIGSTOP信号]

除此之外,守护进程也许需要修改自己的工作目录

  • 而且大部分的服务端都会修改自己的工作目录在根目录下,很可能还会将自己部署到系统中:
  • 总之,是有这个需求的
  • 所以,我们这里也提供支持
  • 也就是使用我们的chdir函数(之前在简单模拟shell中使用过 -- 模拟实现简易版shell(需要单独处理 ls+cd+export)_模拟实现一个简单的shell设计-CSDN博客):

 

实现代码

daemon.hpp

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string null_path = "/dev/null";void daemon(const std::string &path = "")
{// 忽略信号signal(SIGCHLD, SIG_IGN);signal(SIGHUP, SIG_IGN);// 创建子进程if (fork() > 0){exit(0);}// 子进程成为守护进程setsid();// 修改工作目录if (!path.empty()){chdir(path.c_str());}// 重定向int fd = open(null_path.c_str(), O_RDWR);if (fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}
}

log.hpp

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define INFO 0
#define DEBUG 1
#define WARNING 2
#define ERROR 3
#define FATAL 4 // 致命的错误#define SCREEN 1
#define ONEFILE 2#define DEF_NAME "log.txt"
#define DEF_PATH "./log/"#define SIZE 1024class Log
{
public:Log(): method_(SCREEN), path_(DEF_PATH){}void enable(){method_ = ONEFILE;}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);printLog(logtxt);}~Log(){}private:std::string levelToString(int level){switch (level){case INFO:return "INFO";case DEBUG:return "DEBUG";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "NONE";}}void printLog(const std::string &logtxt){switch (method_){case SCREEN:std::cout << logtxt << std::endl;break;case ONEFILE:printOneFile(logtxt);break;default:break;}}void printOneFile(const std::string &info){std::string path = path_ + DEF_NAME;int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);//int fd = open("DEF_NAME", O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd > 0){write(fd, info.c_str(), info.size());close(fd);}else{return;}}private:int method_;std::string path_;
};Log lg;

运行情况

服务端成功变成守护进程(孤儿进程+与终端无关+自成会话)

并且,随着客户端的登录/退出,log.txt也成功增加内容:

守护进程化的接口 -- daemon

参数

他有两个参数:

  • 如果想要把工作目录更换至根目录,nochdir=0;否则为当前路径
  • 如果想要将三个标准流重定向至/dev/null,noclose=0;否则不会对他们做处理

返回值 

​​​​​​​

  • 成功返回0
  • 失败返回-1,并设置错误码

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

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

相关文章

ros找不到生成的可执行文件[rosrun] Couldn‘t find executable named hello_world_cpp below

catkin_make之后source ./devel/setup.bash source之后运行节点的时候,ros找不到可执行文件&#xff08;其实tab键补不齐就没找到了&#xff09; 手动查找发现生成的可执行文件在build下不在devel/lib下&#xff0c;所以白source&#xff0c;压根找不到。 查找原因说是因为CMa…

java项目将静态资源中的文件转为浏览器可访问的http地址

新增一个类叫啥无所谓&#xff0c;主要是实现 WebMvcConfigurer 加上注解 Configuration项目启动时加入bean中 只操作addResourceHandlers这一个方法 其他都没用 文章下方附带一个简易的上传图片代码 package cn.exam.config;import org.springframework.context.annotati…

【P4924】[1007] 魔法少女小Scarlet

[1007] 魔法少女小Scarlet 题目描述 Scarlet 最近学会了一个数组魔法&#xff0c;她会在 n n n\times n nn 二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 9 0 ∘ 90^\circ 90∘。 首先&#xff0c;Scarlet 会把 1 1 1 到 n 2 n^2 n2 的正整数按照从左往右&…

Linux线程补充——周边问题

一、线程池 ​ 使用多线程时要注意传参传递堆空间指针变量&#xff1b; ​ 平常定义的缓冲区就是一个简单的数据池&#xff1b;malloc的底层调用了系统调用来申请堆空间是有成本的&#xff0c;如&#xff1a;需要使用页表和MMU将虚拟地址和物理地址建立映射&#xff0c;期间会…

蓝桥杯刷题记录之数字王国之军训排队

记录 卡了半天&#xff0c;check函数中的temp % ele 0写成了ele % temp 0就挺无语的 思路 这个晚上在补 代码 import java.util.*; public class Main{static List<List<Integer>> que new ArrayList<>();static int MIN Integer.MAX_VALUE;static i…

Personal Website

Personal Website Static Site Generators hexo hugo jekyll Documentation Site Generator gitbook vuepress vitepress docsify docute docusaurus Deployment 1. GitHub Pages 2. GitLab Pages 3. vercel 4. netlify Domain 域名注册 freessl 域名解析域名…

Java基础面试整理

1. Java的跨平台优势&#xff1f; 写好的Java源文件通过Javac命令编译生成class文件(中间文件)&#xff0c;然后JVM对class文件进行执行生成机器语言然后机器语言在平台中操作&#xff0c;Java在不同的平台下都有对应的不同版本的JVM&#xff0c;JVM可以识别字节码文件从而运行…

DMA控制器

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;这是我作为学习笔记的25篇&#xff0c;本篇文章给大家介绍DMA。 无论 I/O 速度如何提升&#xff0c;比起 CPU&#xff0c;总还是太慢。如果我们对于 I/O 的操作&#xff0c;都是由 CPU 发出对应的指令&#xff0c;然后等待…

带气压高度的三点法MATLAB定位函数(基于加权最小二乘法WLS)

函数作用 输入气压高度值、待定位节点与各个锚节点的距离、锚节点位置、权重&#xff08;可选&#xff09;&#xff0c;输出待测点位置 程序源码 function [p_out] triposition_weight_Ver2(dairheight,R_calcu,baseP,varargin) % airghight 由气压高度计算的锚节点-待测节…

【数据结构】线性表的定义与基本操作

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;数据结构 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

用户态和内核态:操作系统权限与运行模式解析

在现代计算机操作系统中&#xff0c;用户态&#xff08;User Mode&#xff09;和内核态&#xff08;Kernel Mode&#xff09;是两种重要的运行模式&#xff0c;用于区分用户程序与操作系统核心之间的权限和特权级别。深入理解这两种模式对于理解操作系统的工作原理至关重要。 …

关于vector的size,unsigned int类型

平时写代码很少注意溢出&#xff0c;这次被遇上了。 题目&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool containsNearbyDuplicate(vector<int>& nums, int k) {//超时,O(n2)// for(int i0;i<nums.size();i)// {// fo…

学习次模函数-第2章 定义

纵观本专著&#xff0c;我们认为及其幂集&#xff08;即&#xff0c; 所有子集的集合&#xff09;&#xff0c;其基数为。我们也考虑一个实值集函数&#xff0c;使得。 与凸函数的一般约定相反&#xff08;见附录A&#xff09;&#xff0c;我们不允许函数有无穷大的值。 次模分…

ssm004新生报到系统+jsp

新生报到系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对新生报到信息管理混乱&#xff0c;出错率…

虚拟线圈法的车辆统计_3.12

目标 车流量统计的方法实现车流量检测 基于虚拟线圈法的车辆统计是一种利用计算机视觉技术模拟传统物理线圈检测原理&#xff0c;对交通视频流中的车辆进行计数的方法。在传统交通监控系统中&#xff0c;物理线圈是通过感应车辆经过时产生的电磁场变化来记录车辆流量。这种方式…

基于深度学习的图像分类方法

基于深度学习的图像分类方法已经成为计算机视觉领域的重要组成部分。这类方法通常涉及使用深度神经网络&#xff0c;特别是卷积神经网络&#xff08;CNNs&#xff09;来识别和分类图像中的对象。以下是基于深度学习的图像分类方法的一些关键要点&#xff1a; 卷积神经网络&…

数据库ER图和ER图转换关系模式

ER图&#xff08;Entity-Relationship Diagram&#xff09;是一种用于描述实体&#xff08;Entity&#xff09;之间关系的图形化工具。ER图是由实体、属性和关系组成的。 实体&#xff08;Entity&#xff09;指的是现实世界中的一个对象或概念&#xff0c;如人、物、事件等。每…

安全问题

信息安全具有不可证明的特性&#xff0c;只能说在某些已知攻击下是安全的&#xff0c;对于将来新的攻击是否安全仍然很难断言。 信息系统不安全的主要因素有程序设计漏洞、用户操作不当和外部攻击。外部攻击形式主要有计算机病毒、恶意软件、黑客攻击等。目前计算机系统在理论…

模型怎么处理不同尺寸的输入图像

1.有全连接层的的CNN模型 卷积能够处理不同尺寸的输入图像&#xff0c;但全连接层不行&#xff0c;因此在送入全连接层之前需将卷积层提取的特征转换为一个固定长度的特征向量。 那么如何转换&#xff1f; 1.1 GAP(Global Average Pooling)全局平均池化 直接代码举例&#…

MySQL数据库备份及恢复

一、数据库备份的分类 1.1 从物理与逻辑的角度 从物理与逻辑的角度&#xff0c;备份可分为物理备份、逻辑备份 物理备份:对数据库操作系统的物理文件(如数据文件日志文件等)的备份 物理备份方法 冷备份(脱机备份)是在关闭数据库的时候进行的 热备份(联机备份):数…