linux网络编程:使用进程池实现TCP多客户服务

    进程池服务:让服务器在启动的某个阶段创建一个进程池,每个客户请求由当前可用子进程池中的某个(闲置)子进程处理。使用进程池的优点在于可以节省fork的开销直接就能处理新到的客户。缺点就是父进程必须父进程必须在服务器启动阶段猜测需要预先派生多少子进程。如果某个时刻客户数恰好等于子子进程总数,那么新到的客户将会被忽略,直到有一个子进程可以被重新使用。如果启动的时候创建过多的子进程,那么就会出现进程池中很多子进程闲置,这样就浪费了系统资源。这里我们暂时不考虑该问题,先看看那线程池实现多客户服务的基本结构。

    在设计线程池的时候需要注意。当多个进程在引用同一个套接字的描述符上调用select时会出现冲突。因为在socket结构中为存放本套接字就绪之时应该唤醒哪些进程而分配的仅仅是一个进程ID的空间。如果有多个进程在等待同一个套接字,那么内核就必须唤醒的是阻塞在select调用中的所有进程。因为它不知道哪些进程受刚变得就绪的这个套接字影响。我们在这里的处理是:在派生子进程之前加上锁,在子进程中调用accept之前获取锁,在accept返回之后释放文件锁。这里提供两种方式上锁:文件上锁,线程上锁。

服务端程序:

/*=============================================================================
#     FileName: processpool.c
#         Desc: The process pool handles multi-client services
#       Author: licaibiao
#   LastChange: 2017-02-15 
=============================================================================*/
#include<stdio.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<unistd.h>  
#include<stdlib.h>  
#include<errno.h>  
#include<arpa/inet.h>  
#include<netinet/in.h>  
#include<string.h>  
#include<signal.h>
#include<unistd.h>
#include <sys/file.h>

#define LISTENLEN 30
#define SERV_PORT 6666

static int		nchildren;
static int		lock_fd = -1;
static pid_t	*pids;
static struct flock	lock_it, unlock_it;


/* fcntl() will fail if my_lock_init() not called */
void my_lock_init(char *pathname)
{
    char	lock_file[1024];

	/* must copy caller's string, in case it's a constant */
    strncpy(lock_file, pathname, sizeof(lock_file));
    lock_fd = mkstemp(lock_file);

    unlink(lock_file);			/* but lock_fd remains open */

	lock_it.l_type = F_WRLCK;
	lock_it.l_whence = SEEK_SET;
	lock_it.l_start = 0;
	lock_it.l_len = 0;

	unlock_it.l_type = F_UNLCK;
	unlock_it.l_whence = SEEK_SET;
	unlock_it.l_start = 0;
	unlock_it.l_len = 0;
}

void my_lock_wait()
{
    int		rc;
    
    while ( (rc = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0) {
		if (errno == EINTR)
			continue;
    	else
			printf("fcntl error for my_lock_wait");
	}
}

void my_lock_release()
{
    if (fcntl(lock_fd, F_SETLKW, &unlock_it) < 0)
		printf("fcntl error for my_lock_release");
}

void web_child(int fd)
{
	char *write = "web_child";

	send(fd,write,strlen(write)+1,0);
}

void child_main(int i, int listenfd, int addrlen)
{
	int				connfd;
	socklen_t		clilen;
	struct sockaddr	*cliaddr;

	cliaddr = malloc(addrlen);

	printf("child %ld starting\n", (long) getpid());
	for ( ; ; ) 
	{
		clilen = addrlen;
		my_lock_wait();
		connfd = accept(listenfd, cliaddr, &clilen);
		my_lock_release();
		web_child(connfd);		/* process the request */
		close(connfd);
	}
}

pid_t child_make(int i, int listenfd, int addrlen)
{
	pid_t	pid;
	void	child_main(int, int, int);

	if ( (pid = fork()) > 0)
		return(pid);		/* parent */

	child_main(i, listenfd, addrlen);	/* never returns */
}

void sig_int(int signo)
{
	int		i;
	void	pr_cpu_time(void);

	/* terminate all children */
	for (i = 0; i < nchildren; i++)
		kill(pids[i], SIGTERM);

	while (wait(NULL) > 0);	/* wait for all children */

	if (errno != ECHILD)
	{
		printf("wait error");
		exit(0);
	}
	//pr_cpu_time();
	exit(0);
}

int main(int argc, char **argv)
{
	int					listenfd, connfd, i;
	const int			on = 1;
	struct sockaddr_in	servaddr;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);

	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	bind(listenfd, (struct sockaddr*) &servaddr, sizeof(servaddr));
	listen(listenfd, LISTENLEN);

	nchildren = LISTENLEN;
	pids = calloc(nchildren, sizeof(pid_t));

	my_lock_init("/tmp/lock.XXXXXX"); /* one lock file for all children */
	for (i = 0; i < nchildren; i++)
		pids[i] = child_make(i, listenfd, sizeof(struct sockaddr_in));	/* parent returns */

	signal(SIGINT, sig_int);

	for ( ; ; )
		pause();	/* everything done by children */
}



客户端测试程序:

/*=============================================================================
#     FileName: client.c
#         Desc: a client for test
#       Author: licaibiao
#   LastChange: 2017-02-15 
=============================================================================*/
#include<stdio.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<unistd.h>  
#include<stdlib.h>  
#include<errno.h>  
#include<arpa/inet.h>  
#include<netinet/in.h>  
#include<string.h>  
#include<signal.h>
#include<unistd.h>
#include <sys/file.h>

#define PORT 	6666
#define MAXLINE 	1024	
#define FOKKNUM		3
#define LOOPNUM		4

int main(int argc, char **argv)
{
	int		i, j, fd, num, ret;
	pid_t	pid;
	char	readbuff[MAXLINE];
	struct sockaddr_in server_addr;

	server_addr.sin_family = AF_INET; 				
	server_addr.sin_port = htons(PORT);				
	inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

	for (i = 0; i < FOKKNUM; i++) 
	{
		if ( (pid = fork()) == 0) /* child */
		{		
			for (j = 0; j < LOOPNUM; j++) 
			{
				fd = socket(AF_INET,SOCK_STREAM,0);
				
				ret = connect(fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
				if(ret < 0)
				{
					printf("connect err \n");
				}

				num = recv(fd,readbuff,MAXLINE,0);
				if(num > 0)
				{
					printf("pid = %d recv data:%s\n",getpid(),readbuff);
				}else
				{
					printf("recv err \n");
				}

				memset(readbuff, 0, MAXLINE);
				close(fd);		/* TIME_WAIT on client, not server */
				//printf("loopnum %d\n",j);
			}
			//printf("child %d done\n", i);
			exit(0);
		}
		/* parent loops around to fork() again */
	}
	while (wait(NULL) > 0)	/* now parent waits for all children */
		;
	if (errno != ECHILD)
		printf("wait error");

	exit(0);
}

测试结果:

root@ubuntu:/home/share/test# ./processpool &
[1] 6760
root@ubuntu:/home/share/test# 
child 6767 starting
child 6769 starting
child 6768 starting
child 6766 starting
child 6771 starting
child 6770 starting
child 6773 starting
child 6772 starting
child 6765 starting
child 6775 starting
child 6774 starting
child 6777 starting
child 6776 starting
child 6764 starting
child 6779 starting
child 6778 starting
child 6781 starting
child 6780 starting
child 6782 starting
child 6784 starting
child 6786 starting
child 6763 starting
child 6785 starting
child 6783 starting
child 6788 starting
child 6787 starting
child 6789 starting
child 6790 starting
child 6762 starting
child 6761 starting

root@ubuntu:/home/share/test# ./client 192.168.0.4
pid = 6794 recv data:web_child
pid = 6794 recv data:web_child
pid = 6793 recv data:web_child
pid = 6794 recv data:web_child
pid = 6793 recv data:web_child
pid = 6794 recv data:web_child
pid = 6793 recv data:web_child
pid = 6793 recv data:web_child
pid = 6792 recv data:web_child
pid = 6792 recv data:web_child
pid = 6792 recv data:web_child
pid = 6792 recv data:web_child
root@ubuntu:/home/share/test# 
root@ubuntu:/home/share/test# 
root@ubuntu:/home/share/test# 

    注意:该程序只是搭建了一个基本的结构,并没有做进程管理。

    文件上锁方法可以移植到所有支持POSIX兼容的系统,不过它涉及文件系统操作,比较的耗时。线程上锁保护accept,这种方法不仅适用于同一进程内各线程之间的上锁,还适用于不同进程之间的上锁。上锁的代码如下:

/* include my_lock_init */
#include	"unpthread.h"
#include	<sys/mman.h>

static pthread_mutex_t	*mptr;	/* actual mutex will be in shared memory */

void my_lock_init(char *pathname)
{
	int		fd;
	pthread_mutexattr_t	mattr;

	fd = Open("/dev/zero", O_RDWR, 0);

	mptr = Mmap(0, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE,
				MAP_SHARED, fd, 0);
	close(fd);

	pthread_mutexattr_init(&mattr);
	pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
	pthread_mutex_init(mptr, &mattr);
}
/* end my_lock_init */

/* include my_lock_wait */
void my_lock_wait()
{
	pthread_mutex_lock(mptr);
}

void my_lock_release()
{
	pthread_mutex_unlock(mptr);
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值