守护进程的创建,服务器关闭不影响客户端的连接

原理:

当关闭服务器时,客户端还能正常运行。

守护进程的特点?

(1)和终端无关。

(2)和输入(键盘)输出(屏幕)无关。

(3)和具体的文件路径无关,实际上它的位置位于根目录。


如何创建守护进程?

(1)摆脱终端

创建一个新的子进程,然后将父亲进程杀死。

通过setsid,创建新的会话,并成为会话的首领。(另立山头)

目的是为了避免自己又再开启一个终端,还要再创建一个新进程,并将父亲进程杀死。

(2)关掉输入输出

将输入输出的描述符跟黑洞绑定。

(3)改变文件路径

(4)设置文件的权限掩码

//服务器守护进程
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

#include <netdb.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <mqueue.h> //消息队列
#include <signal.h>
#include <semaphore.h>
#include <sys/socket.h>  //套接字接口
#include <arpa/inet.h>   //网络地址的转换
#include <time.h>
/*创建守护进程*/
void be_daemon(void)
{
    if(fork()>0)
    {
        //退出父亲进程
        exit(0);
    }
    //这个下面就是子进程干的事

    //创建一个新会话,并成为新会话的首领
    setsid();

    //为了避免会话首领重启终端,那么再创建一个子进程
    if(fork()>0)
    {
        exit(0);
    }
    //这个下面就是子进程的子进程干的事

    chdir("/");//将路径改成根目录

    int i;
    for(i=0;i<3;i++)
    {
        close(i); //关掉输入,输出,错误显示端
    }
    //将三个描述符和黑洞绑定
    ///dev/null这个文件是一个特殊文件,写东西呢进去会被全部吸收,
    //读又读不出来
    open("/dev/null",O_RDWR);//将0这个描述符和null文件绑定
    open("/dev/null",O_RDWR); //将1这个描述符和null文件绑定
    open("/dev/null",O_RDWR); //将2这个描述符和null文件绑定

    //设置文件权限的掩码为0,让进程里的文件默认权限为0777
    umask(0);
}



//发送当前时间
void sendtime(int fd)
{
    char buffer[1024];
    time_t ticks;    
    ticks=time(NULL);//获取从1970年1月1日0时到现在的时间间隔
    snprintf(buffer,sizeof(char)*1024,"%.24s\n",ctime(&ticks));
    write(fd,buffer,strlen(buffer));
}

void sigproc(int signo)
{
    int state;
    //处理子进程
    while(waitpid(-1,&state,0)>0)
    {
        printf("child process has been processed!\n");
    } 
}


int main(int argc,char *argv[])
{
    //注册一个函数,处理结束了的子进程
    signal(SIGCHLD,sigproc);

    be_daemon();

    /*1、创建一个socket*/
    /*int socket(int domain, int type, int protocol);
    domain是地址族,一般用ipv4的地址族,也就是AF_INET
    type表示用tcp还是udp, tcp用SOCK_STREAM, UDP用SOCK_DGRAM
    protocol是协议的意思,一般等于0  
    函数返回的是socket描述符,是一个整型值 
    */
    int sockfd=socket(AF_INET,SOCK_STREAM,0);


    /*2、将上面的套接字描述符和ip地址绑定
    sock地址的准备:
    用到struct sockaddr_in结构体,然后强制转换成struct sockaddr的通用结构体

    绑定:
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    其中,struct sockaddr不好使用,一般用sockaddr_in来准备数据,再强制转换成sockaddr
    */
    struct sockaddr_in sockin; //创建一个sockaddr_in变量
    bzero(&sockin,sizeof(sockin)); //先用0填充
    sockin.sin_family=AF_INET;    //设置地址族
    
    //inet_pton(AF_INET,"127.0.0.1",&sockin.sin_addr);//设置ip地址
    sockin.sin_addr.s_addr=INADDR_ANY;  //上句可以改成这句,表示所有网卡都能接受连接



    sockin.sin_port=htons(2017); //设置端口为2017  
    //以上5句代码是准备sockaddr的数据

    if(bind(sockfd,(struct sockaddr *)&sockin,sizeof(sockin))<0)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    /*
    3、监听客户端的连接,如有则放入队列
    int listen(int sockfd, int backlog);
    backlog可以理解为队列中元素的最大数量    
    */
    if(listen(sockfd,10)<0)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        /*4、从队列中取出一个连接
        int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        addr是连接的客户端地址,是一个输出型的参数
        addrlen是地址结构体的大小,也是输出型参数
        当然,如果不需要,可以设置为NULL

        函数返回一个文件描述符,可以借助这个描述符向客户端读写数据   
        如果队列为空,则本函数阻塞。
        */
        struct sockaddr_in clientaddr;//用来保存客户端的网络地址
        socklen_t clientlen=sizeof(clientaddr);//客户端网络地址的长度


        int fd=accept(sockfd,(struct sockaddr *)&clientaddr,&clientlen);
        if(fd<0)
        {
            perror("accept");
            exit(EXIT_FAILURE);
        }
        /*所谓的多并发,就是能够同时服务多个客户端
        此处,一旦提取了一个连接,则创建一个进程去服务它.
        那父亲进程就可以解放开来,继续回到上面accept下一个连接
        
        */
        if(fork()==0)
        {
            //子进程进来后第一步是将复制了的sockfd关闭,
            //由于有两份sockfd,关掉一个并不会真正关闭连接
            close(sockfd);

            char clientip[20];            
            printf("客户端%s(%d)已连接.\n",
            inet_ntop(AF_INET,&clientaddr.sin_addr,clientip,
            sizeof(clientip)),ntohs(clientaddr.sin_port));

            /*5、用write向客户端发送数据*/
            
            sendtime(fd);
            close(fd);
        }
    }
}
//客户端
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

#include <netdb.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <mqueue.h> //消息队列
#include <signal.h>
#include <semaphore.h>
#include <sys/socket.h>  //套接字接口
#include <arpa/inet.h>   //网络地址的转换
#include <time.h>
//argv[1]是ip地址,argv[2]是端口
int main(int argc, char *argv[])
{
    if(argc<3)
    {
        printf("usage:filename ipaddress port");
        return -1;
    }
    /*1, 创建套接字*/
    int sockfd=socket(AF_INET,SOCK_STREAM,0);

    /*2, 设置网络地址*/
    struct sockaddr_in sockin;
    sockin.sin_family=AF_INET;
    inet_pton(AF_INET,argv[1],&sockin.sin_addr);
    sockin.sin_port=htons(atoi(argv[2]));

    /*3, 连接服务器
    int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);  
    
    */

    if(connect(sockfd,(struct sockaddr *)&sockin, sizeof(sockin))<0)
    {
        perror("connet");
        exit(EXIT_FAILURE);
    }

    /*4, 读取信息*/
    char buffer[1024]={0};    
    if(read(sockfd,buffer,sizeof(buffer))<0)
    {
        perror("read");
    }
    //将信息输出到屏幕上
    write(STDOUT_FILENO,buffer,strlen(buffer));


    close(sockfd);
}
运行结果:

当运行服务器端时,关闭服务器,连接客户端是可以连接成功的,这样就创建了守护进程。

查看守护进程号:ps aux |grep timed

杀死守护进程:kill -9 守护进程号


  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Redis客户端连接配置文件是Redis服务器的配置文件,它包含了Redis服务器的各种配置选项,如监听地址、端口号、密码等。客户端连接Redis服务器时,需要通过配置文件来指定连接的相关参数。以下是一些常见的Redis客户端连接配置选项: 1. bind:指定Redis服务器监听的IP地址,可以是多个IP地址,用空格隔开。 2. port:指定Redis服务器监听的端口号,默认为6379。 3. requirepass:指定Redis服务器连接密码,如果设置了连接密码,则客户端连接Redis服务器时需要输入密码才能进行操作。 4. maxclients:指定Redis服务器最大同时连接数。 5. timeout:指定客户端连接Redis服务器的超时时间。 6. tcp-keepalive:指定TCP连接的keepalive选项,用于检测连接是否断开。 7. daemonize:指定Redis服务器是否以守护进程的方式运行。 8. logfile:指定Redis服务器的日志文件路径。 9. databases:指定Redis服务器支持的最大数据库数量。 在配置文件中,每个配置选项都有一个对应的键值对,如下所示: ``` bind 127.0.0.1 port 6379 requirepass 123456 maxclients 10000 timeout 300 tcp-keepalive 60 daemonize yes logfile /var/log/redis/redis.log databases 16 ``` 以上是一些常见的Redis客户端连接配置选项,你可以根据自己的需求进行配置。如果你想了解更多关于Redis客户端连接配置文件的内容,可以参考Redis官方文档。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值