【socket】基于socket下进程上报温度

fork()函数又叫计算机程序设计中的分叉函数,fork是一个很有意思的函数,它可以建立一个新进程,把当前的进程分为父进程和子进程,新进程称为子进程,而原进程称为父进程。fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程的PID,而子进程中的返回值则返回 0。因此,可以通过返回值来判定该进程是父进程还是子进程。还有一个很奇妙的是:fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。

新创建的子进程几乎但是不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同(但是独立)的一份拷贝,包括文本,数据和bss段、堆以及用户栈。子进程还获得与父进程任何打开文件描述符相同的拷贝。这就是意味着当父进程调用fork时候,子进程还可以读写父进程中打开的任何文件。父进程和新创建的子进程之间最大区别在于他们有着不同的PID。
UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。
下面我们再以一个最简单的代码来更简单说明fork()函数:

#include
#include
int mian(void)
{
   fork();
   printf("hello");
   return 0;
}

每个子进程只有一个父进程,并且每个进程都可以通过getpid()获取自己的进程PID,也可以通过getppid()获取父进程的PID,这样在fork()时返回0给子进程是可取的。一个进程可以创建多个子进程,这样对于父进程而言,他并没有一个API函数可以获取其子进程的进程ID,所以父进程在通过fork()创建子进程的时候,必须通过返回值的形式告诉父进程其创建的子进程PID。这也是fork()系统调用两次返回值设计的原因。

fork()系统调用会创建一个新的子进程,这个子进程是父进程的一个副本。这也意味着,系统在创建新的子进程成功后,会将父进程的文本段、数据段、堆栈都复制一份给子进程,但子进程有自己独立的空间,子进程对这些内存的修改并不会影响父进程空间的相应内存。这时系统中出现两个基本完全相同的进程(父、子进程),这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。如果需要确保让父进程或子进程先执行,则需要程序员在代码中通过进程间通信的机制来自己实现

代码实现

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "sqlite3.h"
#include

#define BACKLOG 13

void print_usage(char *progname);
void sig_stop(int signum);
int socket_listen(char *listen_ip, int listen_serv_port);
void sqlite_tem(char *buf);

static int g_stop = 0;

int main(int argc, char **argv)
{
    int                     rv;
    int                     ret;
    int                     opt;
    int                     serv_port;
    int                     log_fd;
    int                     ch = 1;
    int                     daemon_run = 0;
    int                     listen_fd = -1;
    int                     cli_fd = -1;
    pid_t                   pid = -1;
    struct sockaddr_in      ser_addr;
    struct sockaddr_in      cli_addr;
    socklen_t               cliaddr_len = 20;

    char                    *zErrMsg;
    sqlite3                 *db;
    char                    buf[1024];

    struct option            opts[] = {
        {"daemon", no_argument, NULL, 'd'},
        {"serv_port", required_argument, NULL, 'p'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };

    while ((opt = getopt_long(argc, argv, "dp:h", opts, &ch)) != -1)
    {
        switch(opt)
        {
            case 'd':
                daemon_run = 1;
                break;
            case 'p':
                serv_port = atoi(optarg);
                break;
            case 'h':
                print_usage(argv[0]);
                return 0;
        }
    }

    if (!serv_port)
    {
        print_usage(argv[0]);
        return 0;
    }

    if (daemon_run)
    {
        printf("Program %s is running at the background now\n", argv[0]);

        log_fd = open("fork.log",  O_CREAT|O_RDWR, 0666);
        if (log_fd < 0)
        {
            printf("Open the logfile failure : %s\n", strerror(errno));
            return 0;
        }

        dup2(log_fd, STDOUT_FILENO);
        dup2(log_fd, STDERR_FILENO);

        if ((daemon(1, 1)) < 0)
        {
            printf("Deamon failure : %s\n", strerror(errno));
            return 0;
        }
    }

    signal(SIGUSR1, sig_stop);

    if( (listen_fd = socket_listen(NULL, serv_port)) < 0 )
    {
        printf("ERROR: %s server listen on serv_port %d failure\n", argv[0], serv_port);
        return -2;
    }
    printf("server start to listen on serv_port %d\n",  serv_port);

    while (!g_stop)
    {

        cli_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cliaddr_len);
        if (cli_fd < 0)
        {
            printf("Accept the request from client failure:%s\n", strerror(errno));
            continue;
        }

        pid = fork();
        if (pid < 0)
        {
            printf("Creat child process failure:%s\n", strerror(errno));
            continue;
        }
        else if (pid > 0)
        {
            close(cli_fd);
            continue;
        }
        else if (pid == 0)
        {
            close(listen_fd);

                while (1)
                {
                    memset(buf, 0, sizeof(buf));

                    rv = read(cli_fd, buf, sizeof(buf));
                    if (rv < 0)
                    {
                        printf("Read information from client failure:%s\n", strerror(errno));
                        close(cli_fd);
                        exit(0);
                    }
                    else if (rv == 0)
                    {
                        printf("The connection with client has broken!\n");
                        close(cli_fd);
                        exit(0);
                    }
                    else
                    {
                        printf("%s\n",buf);
                        sqlite_tem(buf);
                        printf("Database inserted successfully!\n");
                    }
                }
        }
    }
    close(listen_fd);

    return 0;
}

void print_usage(char *progname)
{
    printf("-d(--daemon):let program run in the background\n");
    printf("-p(--serv_port):enter server serv_port\n");
    printf("-h(--help):print this help information\n");

    return ;
}

void sig_stop(int signum)
{
    if (SIGUSR1 == signum)
    {
        g_stop = 1;
    }
    return ;
}

int socket_listen(char *listen_ip, int listen_serv_port)
{
    int                     rv = 0;
    int                     on = 1;
    int                     listen_fd;
    struct sockaddr_in      servaddr;

    if ( (listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
        return -1;
    }

    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(listen_serv_port);

    if( !listen_ip )
    {
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    else
    {
        if( inet_pton(AF_INET, listen_ip, &servaddr.sin_addr)  0 )
        {
            printf("Inet_pton() set listen IP address failure\n");
            rv = -2;
            goto CleanUp;
        }
    }

    if( bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
    {
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
        rv = -3;
        goto CleanUp;
    }

    if( listen(listen_fd, 64) < 0 )
    {
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
        rv = -4;
        goto CleanUp;
    }

CleanUp:
    if( rv < 0 )
        close(listen_fd);
    else
        rv = listen_fd;
    return rv;
}

void sqlite_tem(char *buf)
{
    int             nrow=0;
    int             ncolumn = 0;
    char          **azResult=NULL;
    int             rv;
    sqlite3        *db=NULL;
    char           *zErrMsg = 0;
    char            sql1[100];
    char           *ipaddr=NULL;
    char           *datetime=NULL;
    char           *temper=NULL;
    char           *sql = "create table if not exists temperature(ipaddr char(30), datetime char(50), temper  char(30))";

    ipaddr = strtok(buf,"/");
    datetime = strtok(NULL, "/");
    temper = strtok(NULL, "/");

    rv = sqlite3_open("tempreture.db", &db);
    if(rv)
    {
        printf("Can't open database:%s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return;
    }
    printf("opened a sqlite3 database named tempreture.db successfully!\n");

    int ret = sqlite3_exec(db,sql, NULL, NULL, &zErrMsg);
    if(ret != SQLITE_OK)
    {
        printf("create table fail: %s\n",zErrMsg);
    }

    if(snprintf(sql1,sizeof(sql1), "insert into temper values('%s','%s','%s')", ipaddr, datetime, temper) < 0)
    {
        printf("Failed to write data\n");
    }

    sqlite3_exec(db, sql1, 0, 0, &zErrMsg);
    sqlite3_free(zErrMsg);
    sqlite3_close(db);
    return;
}

在该程序中,父进程accept()接收到新的连接后,就调用fork()系统调用来创建子进程来处理与客户端的通信。因为子进程会继承父进程处于listen状态的socket 文件描述符(sockfd),也会继承父进程accept()返回的客户端socket 文件描述符(cli_fd),但子进程只处理与客户端的通信,这时他会将父进程的listen的文件描述符sockfd关闭;同样父进程只处理监听的事件,所以会将cli_fd关闭。

此时父子进程同时运行完成不同的任务,子进程只负责跟已经建立的客户端通信,而父进程只用来监听到来的socket客户端连接。所以当有新的客户端到来时,父进程就有机会来处理新的客户连接请求了,同时每来一个客户端都会创建一个子进程为其服务。

Original: https://www.cnblogs.com/Ye-Wei/p/16728601.html
Author: 西故黄鹤楼
Title: 【socket】基于socket下进程上报温度

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/606348/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Linux-shell

    shell 为注释 !执行脚本的 解释器 为bash 权限问题 remark 调试 bash -x man test 变量 —>默认必须全局变量 局部变量必须给local a…

    Linux 2023年6月7日
    074
  • STP 指定端口 根端口 区别和理解

    不多说,先上图,A为指定端口,B为非指定端口。 看本文的网友应该知道根端口和指定端口的选举,但是对指定端口和根端口的理解不清楚。这里我就略过选举过程,直接描述这两者的区别和存在的意…

    Linux 2023年6月6日
    0137
  • 浅谈kali : arpspoof工具原理

    介绍 arpspoof是一个通过ARP协议伪造数据包实现中间人攻击的kali工具。 中间人攻击虽然古老,但仍处于受到黑客攻击的危险中,可能会严重导致危害服务器和用户。仍然有很多变种…

    Linux 2023年6月14日
    076
  • 一劳永逸,解决.NET发布云服务器的时区问题

    国内大多数开发者使用的电脑,都是使用的北京时间,日常开发的过程中其实并没有什么不便;不过,等遇到了阿里云等云服务器,系统默认使用的时间大多为 UTC时间,这个时候,时区和时间的问题…

    Linux 2023年6月6日
    089
  • centos 安装bochs

    下载地址 https://sourceforge.net/projects/bochs/files/bochs/ 我这里安装2.6.2 tar xvfz bochs-2.6.2.t…

    Linux 2023年6月7日
    083
  • 【电子取证:FTK Imager篇】FTK Imager制作镜像详细介绍

    以DD镜像制造为例,详细介绍了FTK Imager创建镜像的过程,记得大学的时候学习这些没什么教程,找到的资料也是语焉不详,故在此啰嗦一番—【suy】 一、磁盘镜像制作…

    Linux 2023年6月13日
    01.3K
  • SSH的 Write failed: Broken pipe 问题

    问题现象: 表示连接管道已经断开 解决方法: 方法一:客户端配置在客户端的 ~/.ssh/ config文件(如不存在请自行创建)中添加下面内容:ServerAliveInterv…

    Linux 2023年6月8日
    084
  • 【总结】瞬时高并发(秒杀/活动)Redis方案

    1,Redis 丰富的数据结构(Data Structures) * 字符串(String) – Redis字符串能包含 任意类型的数据 一个字符串类型的值最多能存储 …

    Linux 2023年5月28日
    076
  • 关于在Rocky linux下安装dotnet sdk不成功的问题

    Rocky Linux 9,运行 dnf install -y dotnet-sdk-6.0 一切正常,运行起来非常顺利,安装完毕。但是非常诡异,运行 dotnet –list-…

    Linux 2023年6月6日
    0109
  • LeetCode-459. 重复的子字符串

    题目来源 题目详情 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。 示例 1: 输入: s = “abab”输出: true解释:…

    Linux 2023年6月7日
    0114
  • redis订阅关闭异常解决

    redis订阅关闭异常解决 应用程序模块订阅redis运行一段时间出现一直重连Redis服务,日志如下: 2019-04-28 10:06:17,551 ERROR org.spr…

    Linux 2023年5月28日
    0111
  • 4.1 打包和压缩的概念和区别

    在讲解具体的归档命令和压缩命令之前,先来了解一下归档和压缩所各自代表的含义。 归档,也称为打包,指的是一个文件或目录的集合,而这个集合被存储在一个文件中。归档文件没有经过压缩,因此…

    Linux 2023年6月7日
    081
  • 2020年12月-第02阶段-前端基础-CSS Day07

    CSS Day07 CSS高级技巧 *理解 能说出元素显示隐藏最常见的写法能说出精灵图产生的目的能说出去除图片底侧空白缝隙的方法 *应用 能写出最常见的鼠标样式能使用精灵图技术能用…

    Linux 2023年6月8日
    0111
  • Ubuntu16.04部署django+nginx项目

    项目使用django+nginx部署。这个项目断断续续地部署4遍了。感觉每次部署都挺费时间的(找各种配置的资料),于是写一个博客总结一下。 安装vsftpd $ sudo apt-…

    Linux 2023年6月7日
    061
  • iptables快速入门

    iptables简介 在生产实践过程中,为了保证生产安全,通常需要进行数据加密,例如通过网络层面上将应用隔离成内网和公网应用,内网再分成生产、测试、开发、DMZ等区域,然后进行不同…

    Linux 2023年6月6日
    088
  • centos7 安装MariaDB 10.6

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 背景 centos7使用yum install mariadb-server命令安装的默认版本是5.5的,这是因为系统默认源只有…

    Linux 2023年5月27日
    0355
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球