多进程服务端进程框架

在这里插入图片描述

对父进程来说,不需要客户端连接的socket,对子进程来说,不需要监听的socket。代码中标出了两行关闭对应socket的函数
在这里插入图片描述
上图可见,对应的关闭了以后(3是服务端监听的socket,4是客户端连上来的socket)
在这里插入图片描述
通过上图测试,如果进行对应的关闭,当同时运行多个客户端(测试了3个)时,就会一直占用id。

// 演示多进程服务端程序
#include <iostream>
#include <signal.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>


using namespace std;

class ctcpserver
{
    private:
        int m_listenfd;         // 监听的socket,-1表示未初始化
        int m_clientfd;         // 客户端连接上来的socket,-1表示客户端未连接
        string m_clientip;      // 客户端的IP
        unsigned short m_port;  // 服务端用于通讯的端口
    public:
        ctcpserver():m_listenfd(-1), m_clientfd(-1) {}

        // 初始化服务端用于监听的socket
        bool initserver(const unsigned short in_port)
        {   // 1 创建服务端socket
            if((m_listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
                return false;
            m_port = in_port;
            // 2 把服务端用于通信的IP和端口绑定到socket上
            struct sockaddr_in servaddr;            // 用于存放协议、端口和IP地址的结构体
            memset(&servaddr, 0, sizeof(servaddr));
            servaddr.sin_family = AF_INET;          // 协议族,固定填AF_INET
            servaddr.sin_port = htons(m_port);      // 指定服务端的通信端口
            servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 如果操作系统有多个IP,全部的IP都可以用于通讯
            // 绑定服务端的IP和端口(为socket分配IP和端口)
            if(bind(m_listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) // 绑定服务端的IP和端口
            {
                close(m_listenfd);
                m_listenfd = -1;
                return false;
            }
            // 3 把socket设置可为连接(监听)的状态
            if(listen(m_listenfd, 5) == -1)
            {
                close(m_listenfd);
                m_listenfd = -1;
                return false;
            }
            return true;
        }

        // 受理客户端的连接(从已连接的客户端中取出一个客户端)
        // 如果没有已连接的客户端,accept()函数将阻塞等待
        bool accept()
        {
            struct sockaddr_in caddr;
            socklen_t addrlen = sizeof(caddr);
            if((m_clientfd = ::accept(m_listenfd, (struct sockaddr *)&caddr, &addrlen)) == -1)
                return false;
                
            m_clientip = inet_ntoa(caddr.sin_addr);

            return true;
        }

        // 获取客户端的IP(字符串格式)
        const string & clientip() const
        {
            return m_clientip;
        }

        // 向客户端发送报文,成功返回true,失败返回false
        bool send(const string &buffer)
        {
            if(m_clientfd == -1)
                return false;
            if((::send(m_clientfd, buffer.data(), buffer.size(), 0)) <= 0)
                return false;
                
            return true;
        }

        // 接受对端的报文,成功返回true,失败返回false
        // buffer存放接收到的报文内容,maxlen本次接收报文的最大长度
        bool recv(string &buffer, const size_t maxlen)
        { // 如果直接操作string对象的内存,必须保证:1)不能越界;2)操作后手动设置数据的大小
            buffer.clear();
            buffer.resize(maxlen);
            int readn = ::recv(m_clientfd, &buffer[0], buffer.size(), 0); // 直接操作buffer的内存
                    //readn表示接受到的数据大小,-1失败,0 socket连接已断开,>0 成功接收到数据
            if(readn <= 0)
            {
                buffer.clear();
                return false;
            }
            buffer.resize(readn); // 重置buffer的实际大小

            return true;
        }

        //关闭监听的socket
        bool closelisten()
        {
            if(m_listenfd == -1)
                return false;
                
            ::close(m_listenfd);
            m_listenfd = -1;
            return true;
        }
        // 关闭客户端连上来的socket
        bool closeclient()
        {
            if(m_clientfd == -1)
                return false;

            ::close(m_clientfd);
            m_clientfd = -1;
            return true;
        }

        ~ctcpserver()
        {
            closelisten();
            closeclient();
        }
};

void FathEXIT(int sig);
void ChldEXIT(int sig);

ctcpserver tcpserver; // 定义为全局对象,方便在父进程子进程函数中使用。

int main (int argc, char *argv[])
{
    if(argc != 2)
    {
        cout << "Using:./demo_server 通讯宽口\nExample:./demo_server 5005\n\n";
        cout << "注意:运行服务端程序的Linux系统的防火墙必须要开通5005端口。\n";
        cout << "如果是云服务器,还要开通云平台访问策略。\n\n";
        return -1;
    }

    // 忽略全部信号,不希望被打扰
    for(int ii = 1; ii <= 64; ii ++ )
        signal(ii, SIG_IGN);
    signal(SIGTERM, FathEXIT); // 15
    signal(SIGINT, FathEXIT);  // 2

    
    if(tcpserver.initserver(atoi(argv[1])) == false)
    {
        perror("initserver()");
        return -1;
    }
    while(true)
    {      
        // 受理客户端的连接(从已连接的客户端中取出一个客户端)
        // 如果没有已连接的客户端,accept()函数将阻塞等待
        if(tcpserver.accept() == false)
        {
            perror("accept()");
            return -1;
        }

        int pid = fork();
        if(pid == -1) // 系统资源不足
        {
            perror("accept()");
            return -1;
        }
        if(pid > 0) 
        {   // 父进程
            tcpserver.closeclient(); // 关闭客户端连接的socket(父进程不需要了)
            continue; // 父进程返回到循环开始的位置,继续受理客户端的连接
        }
        tcpserver.closelisten(); // 子进程也不需要监听的socket了
        //子进程需要重新设置信号
        signal(SIGTERM, ChldEXIT); // 子进程的退出函数与父进程不一样
        signal(SIGINT, SIG_IGN); // 子进程不需要捕获SIGINT信号
        
        // 子进程负责与客户端进行通讯
        cout << "客户端已连接(" << tcpserver.clientip() << ")。\n";

        string buffer;
        
        while(true)
        {
            
            // 接收客户端请求报文,如果客户端没有发送请求报文,recv()函数将阻塞等待
            // 如果客户端已断开连接,recv()函数将返回0
            if((tcpserver.recv(buffer, 1024)) == false)
            {
                perror("recv()");
                break;
            }
            cout << "接收:" << buffer << endl;
            buffer = "OK";
            if(tcpserver.send(buffer) == false) // 向客户端发送回应报文
            {
                perror("send"); 
                break;
            }
            cout << "发送:" << buffer << endl;
        }

        return 0; // 子进程一定要退出,不然又会回到accept()函数的位置
    }
    
}

void FathEXIT(int sig) // 父进程处理信号
{
    // 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断
    signal(SIGINT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);

    cout << "父进程退出,sig = " << sig << endl;

    kill(0, SIGTERM); // 向全部的子进程发送15的信号,通知他们退出

    // 增加释放资源的代码
    tcpserver.closelisten(); // 父进程退出时关闭监听的socket
     
    exit(0);
}

void ChldEXIT(int sig)
{
    // 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断
    signal(SIGINT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);

    cout << "子进程" << getpid() << "退出,sig = " << sig << endl;

    tcpserver.closeclient(); // 子进程退出时关闭连接上来的客户端的socket

    exit(0);
}

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

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

相关文章

【GPTs分享】每日GPTs分享之Image Generator Tool

今日GPTs分享&#xff1a;Image Generator Tool。Image Generator Tool是一种基于人工智能的创意辅助工具&#xff0c;专门设计用于根据文字描述生成图像。这款工具结合了专业性与友好性&#xff0c;鼓励用户发挥创造力&#xff0c;同时提供高效且富有成效的交互体验。 主要功能…

10:00面试,10:05就出来了,问的问题过于变态了。。。

我从一家小公司转投到另一家公司&#xff0c;期待着新的工作环境和机会。然而&#xff0c;新公司的加班文化让我有些始料未及。虽然薪资相对较高&#xff0c;但长时间的工作和缺乏休息使我身心俱疲。 就在我逐渐适应这种高强度的工作节奏时&#xff0c;公司突然宣布了一则令人…

C++ Webserver从零开始:代码书写(十四)——http连接处理

前言 HTTP类是Webserver到目前为止最为庞大的类。其实最开始我是只想分析它的部分代码&#xff0c;但是最后我还咬咬牙将http连接处理的全代码分析写完了。因此&#xff0c;本文会特别的长&#xff0c;我相信没人可以把它一口气全部读完。不过我在本文中进行了细致的目录划分&a…

部署roop实现视频人脸替换

roop只需要一张人脸的图像&#xff0c;就可以替换视频中的脸。不需要数据集和模型训练。 下载对应版本的cudnn https://developer.nvidia.com/rdp/cudnn-archivehttps://developer.nvidia.com/rdp/cudnn-archive解压后的三个文件夹拷贝到cuda的目录 C:\Program Files\NVIDIA…

【非递归版】归并排序算法(2)

目录 MergeSortNonR归并排序 非递归&归并排序VS快速排序 整体思想 图解分析​ 代码实现 时间复杂度 归并排序在硬盘上的应用&#xff08;外排序&#xff09; MergeSortNonR归并排序 前面的快速排序的非递归实现&#xff0c;我们借助栈实现。这里我们能否也借助栈去…

uniapp实现单选框

采用uniapp-vue3实现的一款单选框组件&#xff0c;提供丝滑的动画选中效果&#xff0c;支持不同主题配置&#xff0c;适配web、H5、微信小程序&#xff08;其他平台小程序未测试过&#xff0c;可自行尝试&#xff09; 可到插件市场下载尝试&#xff1a; https://ext.dcloud.net…

SpringCloudAlibaba全家桶介绍

Spring Cloud Alibaba Spring Cloud Alibaba 是什么&#xff1f;微服务全景图核心特色 大家好&#xff0c;我叫阿明。下面我会为大家准备Spring Cloud Alibaba系列知识体系&#xff0c;结合实战输出案列&#xff0c;让大家一眼就能明白得技术原理&#xff0c;应用于各公司得各…

二次供水物联网:HiWoo Cloud助力城市水务管理升级

随着城市化的快速推进&#xff0c;二次供水系统作为城市基础设施的重要组成部分&#xff0c;其稳定运行和高效管理显得至关重要。然而&#xff0c;传统的二次供水管理方式在应对复杂多变的城市供水需求时&#xff0c;显得力不从心。为了破解这一难题&#xff0c;HiWoo Cloud平台…

VsCode的leetcode插件无法登录

前提 想使用VsCode的leetcode插件进行刷题&#xff0c;然后按照网上的教程进行安装下载&#xff0c;但是到了登录这一步&#xff0c;死活也登录不了&#xff0c;然后查看log一直报的错误是invalid password。 解决方法 首先确定在插件中设置的站点是Leetcode中国&#xff0c…

【Java EE初阶二十五】简单的表白墙(一)

1. 前端部分 1.1 前端代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wid…

继电器测试中需要注意的安全事项有哪些?

继电器广泛应用于电气控制系统中的开关元件&#xff0c;其主要功能是在输入信号的控制下实现输出电路的断开或闭合。在继电器测试过程中&#xff0c;为了确保测试的准确性和安全性&#xff0c;需要遵循一定的安全事项。以下是在进行继电器测试时需要注意的安全事项&#xff1a;…

【代码随想录python笔记整理】第十三课 · 链表的基础操作 1

前言:本笔记仅仅只是对内容的整理和自行消化,并不是完整内容,如有侵权,联系立删。 一、链表 在之前的学习中,我们接触到了字符串和数组(列表)这两种结构,它们具有着以下的共同点:1、元素按照一定的顺序来排列。2、可以通过索引来访问数组中的元素和字符串中的字符。由此,…

go环境安装-基于vscode的Windows安装

1、vscode安装 官网链接&#xff1a;https://code.visualstudio.com/ 选择相应的版本&#xff0c;这里选择Windows下的 下载得到一个VSCodeUserSetUp-x64的可执行文件&#xff0c;双击执行&#xff0c;选择要安装的路径&#xff0c;下一步。 2、go语言安装 官网链接&#x…

后端程序员入门react笔记(五)ajax请求

常见的ajax Ajax 最原始的方式&#xff0c;基于原生的js XmlHttpRequest 多个请求之间如果有先后关系&#xff0c;会存在很多层回调的问题&#xff0c;也是基于原生js Jquery Ajax 基于原生XHR封装&#xff0c;依赖Jquery框架&#xff0c;由jquery 框架去封装原生的XML(Xml)封…

git commit 后,本地远端都没有记录,消失不见

今天git commit 之后发现远端没有记录&#xff0c;本地没有最新代码记录 git commit 后&#xff0c;提交记录会消失不见的原因可能是&#xff1a; git只git commit了&#xff0c;没有push到远程分支&#xff0c;切换到其他分支时丢失。而且看不到提交记录&#xff0c;和找不到…

命令执行 [UUCTF 2022 新生赛]ez_rce

打开题目 得到题目源码 居然都不输入参数&#xff0c;可恶!!!!!!!!!<?php ## 放弃把&#xff0c;小伙子&#xff0c;你真的不会RCE,何必在此纠结呢&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#…

2024首更,Smartbi产品功能更新,用户体验更丝滑

Smartbi用户可以在官网下载Smartbi V11最新版本&#xff08;https://www.smartbi.com.cn/download&#xff09;&#xff08;PC端下载&#xff09;更新后可以使用相关功能&#xff0c;也可以在思迈特官网体验中心体验相关功能。 交互仪表盘 ▍指标拆解树组件支持从右到左展开指标…

应用回归分析:弹性网络回归

弹性网络回归&#xff1a;原理、优势与应用 弹性网络回归&#xff08;Elastic Net Regression&#xff09;是一种广泛使用的线性回归方法&#xff0c;它结合了岭回归&#xff08;Ridge Regression&#xff09;和套索回归&#xff08;Lasso Regression&#xff09;的特点。通过…

搭建Facebook直播网络对IP有要求吗?

在当今数字化时代&#xff0c;Facebook直播已经成为了一种极具吸引力的社交形式&#xff0c;为个人和企业提供了与观众直接互动的机会&#xff0c;成为推广产品、分享经验、建立品牌形象的重要途径。然而&#xff0c;对于许多人来说&#xff0c;搭建一个稳定、高质量的Facebook…

算法竞赛--对拍

对拍需要 loop.bat、makedate.exe、a.in、a.exe、a.out、std.exe、std.out ,注意这几个文件要全部在同一文件夹下。 loop.bat 比较代码&#xff08;在记事本里写&#xff0c;后缀改成.bat) :loopmakedataastdfc std.out a.outif %errorlevel%0 goto loop pause makedata.exe…
最新文章