服务端并发处理客户端请求,并回收子进程

1.直接贴上源码:

        注:此处使用了对常见的网络编程函数异常处理后的头文件wrap.c(下载于网络)

// 目的:使用多进程处理客户端发来的请求
// 步骤:
// 		1.socket()创建监听文件描述符 lfd
//		2.bind() 将lfd与ip和port绑定
//		3.listen() 设置为被动监听模式
//		4.while(1)
//		{
// 			cfd = accept()
// 			pid = fork()创建子进程
//			父进程,回收子进程
//			子进程,循环读写,循环退出时,注意:子进程不能再循环去创建子进程
// 		}
// 		

// <使用自定义的头文件实现>
//
#include "wrap.h"
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>

// 回收子进程的信号处理函数。
// 可以应对的场景有:
// 		1.在绑定信号处理函数之前,子进程已全部退出
//		2.绑定信号处理函数之前,子进程退出了一部分,之后子进程相继推出
//		3.绑定之前都为退出,之后陆续退出(其实和第二点一样啦~~)
//   pid_t waitpid(pid_t pid, int *wstatus, int options);
void cleanSons()
{
    while(1)
        {
            // sleep(1);
            pid_t ret = waitpid(-1,NULL,WNOHANG);
            if(ret > 0)
            {
                printf("已回收掉进程[%d]\n",ret);
            }
            else if(ret == 0)
            {
                // printf("~_~\t");
                printf("子进程在运行\n");
                break;     // 对应一个一个退出的情况!!!
            }
            else if(ret < 0)
            {
                printf("子进程已全部回收\n");
                break;
            }
        }
}



int main()
{
	// --1--生成监听文件描述符 int socket(int domain, int type, int protocol);
	int lfd = Socket(AF_INET, SOCK_STREAM, 0);

	// --2--绑定端口 int bind(int sockfd, const struct sockaddr *addr,
	// socklen_t addrlen);
	struct sockaddr_in serv;
	bzero(&serv, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	Bind(lfd, (struct sockaddr*)&serv, sizeof(serv));

	// --3-- 设置为监听模式 int listen(int sockfd, int backlog);
	Listen(lfd, 128);

	// --4-- 进入while循环,监听连接请求,建立相应子进程
	int cfd;
	socklen_t len;
	pid_t pid;
	while(1)
	{
		// --接受连接,建立子进程
		// --接受连接--int accept(int sockfd, struct sockaddr *addr, 
		// socklen_t *addrlen);
		struct sockaddr_in client;
		len = sizeof(client);
		cfd = Accept(lfd, (struct sockaddr *)&client, &len);

		// 打印客户端的信息
		char IP[16];
		inet_ntop(AF_INET, &client.sin_addr.s_addr, IP, sizeof(IP));
		printf("client IP = [%s], port = [%d]", IP, ntohs(client.sin_port));
	
		 // 创建子进程之前,先阻塞SIGCHLD信号,避免子进程回收函数注册之前,子进程已全部退出。避免子进程成为僵尸进程
    	sigset_t mask;
    	sigemptyset(&mask);
   	 	sigaddset(&mask,SIGCHLD);
    	sigprocmask(SIG_BLOCK,&mask,NULL);
		// --fork子进程 --
		pid = fork();
		if(pid < 0)
		{
			perror("fork error");
			return -1;
		}
		else if(pid > 0)
		{
			//关闭连接文件描述符
			close(cfd);
			printf("hello\n");
			//回收子进程
			//注册子进程回收信号处理函数
        	// int sigaction(int signum, const struct sigaction *act,
        	//              struct sigaction *oldact);
        	struct sigaction act;
        	act.sa_handler = cleanSons;
        	sigemptyset(&act.sa_mask);
        	sigaddset(&act.sa_mask,SIGQUIT);
        	act.sa_flags = 0;

        	sigaction(SIGCHLD,&act,NULL);
        	sigprocmask(SIG_UNBLOCK,&mask,NULL); //解除对SIGCHLD的屏蔽
		}
		else if(pid == 0)
		{
			//关闭通信文件描述符
			close(lfd);
			// 处理业务逻辑 读写
			char buf[128];
			int n = 0;
			int i = 0;

			while(1)
			{
				memset(buf, 0x00, sizeof(buf));
				n = Read(cfd, buf, sizeof(buf));
				printf("%s\n", buf);

				if(n <= 0)
				{
					perror("read error or client closed");
					break;
				}
				for(i = 0; i < n; i++)
				{
					buf[i] = toupper(buf[i]);
				}

				Write(cfd, buf, sizeof(buf));

			}
			// 读错误退出业务逻辑
			close(cfd);
			exit(-1);   // 也可以 break,退出外面的 while(1)循环,接着就退出了整个子进程
			            // exit() 更直观一点,表示到此没有什么要处理的了,直接退出整个进程吧~
		}
	}


	return 0;
}

2.头文件

        可以直接搜索 wrap.c 得到 头文件的实现代码 wrap.c 和头文件的声明 wrap.h (emm~贴上来太长了,自行下载哦)

3.实验:

        ①:首先运行起来服务端:

       ②:接着创建三个客户端连接服务端:


 

        ③:接着查看一下相关进程信息:

可以看到父进程已经拉起了3个子进程。

        ④:

下面就要测试一下关掉一个客户端后,break退出子进程在 waile(1) 的业务逻辑,接着 exit(-1) 后子进程退出。(简单说就是 关闭一个客户端,使得一个子进程退出)

父进程是否能对子进程进行回收。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

好了,试一下吧~

再查看相关进程信息:

关闭了两个客户端,杀死了两个子进程,没有出现僵尸进程。

再看一下服务端的日志打印:

也打印出了回收的子进程,是相互印证的。

over ~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值