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

进程温度通信

fork系统调用

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

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

#include <stdio.h>
#include <unistd.h>
int mian(void)
{
   fork();
   printf("hello");
   return 0;
}

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

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

代码实现

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "sqlite3.h"
#include <sqlite3.h>

#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);

    /*调用socket*/
    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) /*父进程关闭cli_fd,保留listen_fd,等待与其他客户端连接*/
        {
            close(cli_fd);  
            continue;
        }
        else if (pid == 0) /*子进程关闭ser_fd,保留listen_fd,保持与客户端连接*/
        {
            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 ;
}

/*socket函数*/
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");
    }
    //printf("Write data successfully!\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客户端连接。所以当有新的客户端到来时,父进程就有机会来处理新的客户连接请求了,同时每来一个客户端都会创建一个子进程为其服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值