• UNIX网络编程——客户/服务器程序设计示范(二)


        TCP并发服务器程序,每个客户一个子进程

          

                 

             传统上并发服务器调用fork派生一个子进程来处理每个客户。这使得服务器能够同时为多个客户服务,每个进程一个客户。客户数目的唯一限制是操作系统对以其名义运行服务器的用户ID能够同时有多个子进程的限制。并发服务器的问题在于为每个客户现场fork一个子进程比较耗费CPU时间。

             

            本函数为每个客户连接fork一个子进程并处理来自垂死的子进程的SIGCHLD信号。我们还捕获由键入终端中断按键产生的SIGINT信号。在客户运行完毕之后我们键入该键以显示服务器程序运行所需的CPU时间。下面给出了SIGINT信号处理函数。这是一个信号处理函数不返回而直接终止进程。
           getrusage函数被调用了两次,分别返回调用进程(RUSAGE_SELF)和它的所有已终止子进程(RUSAGE_CHILDREN)的资源利用统计。所显示的值包括总的系统时间(内核在代表调用进程执行系统调用上耗费的CPU时间)。
            客户在建立与服务器的连接之后通过该连接写出一行文本,指出需由服务器发送多少字节的数据给客户。这一点与HTTP有些类似:客户发送一个小请求,服务器响应以所期望的信息(例如一个HTML文件或一副GIF图片),在HTTP应用系统中,服务器通常在发送回所请求的数据之间就关闭连接,不过较新的版本允许使用持续连接,为在某个时限以内到达的额外客户请求继续保持TCP连接开发一段时间。
            在web_child函数中,服务器允许来自客户的额外请求。

    #include	"unp.h"
    void pr_cpu_time(void)
    {
    	double			user, sys;
    	struct rusage	myusage, childusage;
    
    	if (getrusage(RUSAGE_SELF, &myusage) < 0)
    		err_sys("getrusage error");
    	if (getrusage(RUSAGE_CHILDREN, &childusage) < 0)
    		err_sys("getrusage error");
    
    	user = (double) myusage.ru_utime.tv_sec +
    					myusage.ru_utime.tv_usec/1000000.0;
    	user += (double) childusage.ru_utime.tv_sec +
    					 childusage.ru_utime.tv_usec/1000000.0;
    	sys = (double) myusage.ru_stime.tv_sec +
    				   myusage.ru_stime.tv_usec/1000000.0;
    	sys += (double) childusage.ru_stime.tv_sec +
    					childusage.ru_stime.tv_usec/1000000.0;
    
    	printf("
    user time = %g, sys time = %g
    ", user, sys);
    }
    
    void sig_int(int signo)
    {
    	void	pr_cpu_time(void);
    
    	pr_cpu_time();
    	exit(0);
    }
    
    void sig_chld(int signo)
    {
    	pid_t	pid;
    	int		stat;
    
    	while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
    		printf("child %d terminated
    ", pid);
    	return;
    }
    
    #define	MAXN	16384		/* max # bytes client can request */
    
    void web_child(int sockfd)
    {
    	int			ntowrite;
    	ssize_t		nread;
    	char		line[MAXLINE], result[MAXN];
    
    	for ( ; ; ) {
    		if ( (nread = Readline(sockfd, line, MAXLINE)) == 0)
    			return;		/* connection closed by other end */
    
    			/* 4line from client specifies #bytes to write back */
    		ntowrite = atol(line);
    		if ((ntowrite <= 0) || (ntowrite > MAXN))
    			err_quit("client request for %d bytes", ntowrite);
    
    		Writen(sockfd, result, ntowrite);
    	}
    }
    
    int main(int argc, char **argv)
    {
    	int					listenfd, connfd;
    	pid_t				childpid;
    	void				sig_chld(int), sig_int(int), web_child(int);
    	socklen_t			clilen, addrlen;
    	struct sockaddr		*cliaddr;
    
    	if (argc == 2)
    		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
    	else if (argc == 3)
    		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
    	else
    		err_quit("usage: serv01 [ <host> ] <port#>");
    	cliaddr = Malloc(addrlen);
    
    	Signal(SIGCHLD, sig_chld);
    	Signal(SIGINT, sig_int);
    
    	for ( ; ; ) {
    		clilen = addrlen;
    		if ( (connfd = accept(listenfd, cliaddr, &clilen)) < 0) {
    			if (errno == EINTR)
    				continue;		/* back to for() */
    			else
    				err_sys("accept error");
    		}
    
    		if ( (childpid = Fork()) == 0) {	/* child process */
    			Close(listenfd);	/* close listening socket */
    			web_child(connfd);	/* process request */
    			exit(0);
    		}
    		Close(connfd);			/* parent closes connected socket */
    	}
    }


  • 相关阅读:
    《ASP.NET1200例》实现投票的用户控件
    《转》这些年这些感悟
    《转》不要过打折的生活,当你发现这些你有了,说明你开始成熟了
    HTML控件ID和NAME属性及在CS页面获得.ASPX页面中HTML控件的值
    逻辑回归(1)
    MySQL笔记5-----索引(覆盖索引等)
    MySQL笔记4------面试问题
    MySQL-----笔记3:存储引擎
    Python可视化数据------seaborn
    树(2)-----leetcode(层、深度、节点)
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6172539.html
Copyright © 2020-2023  润新知