TCP网络编程之服务器程序设计范例

本文介绍了五种TCP并发服务器程序设计方式,包括accept + fork、accept + thread、线程池、Reactor + thread-per-task以及Reactors in threads,探讨了它们的优缺点和并发处理策略,是网络编程学习的重要参考资料。
摘要由CSDN通过智能技术生成

1、accept + fork, TCP并发服务器程序
每个客户一个子进程
存在的问题是每个客户现场fork 一个子进程比较悍匪CPU时间。

此类型并发服务器的主体循环伪代码如下

for(;;){
	clilen = addrlen;
	if((confd = accept(listenfd,cliaddr,&clilem)) < 0)
	{
		if(errno == EINTR)
			continue;
		else
			err_sys("accept error");
	}
	if((childpid = fork()) == 0)
	{
		close(listenfd);//关闭子进程的监听描述符
		web_child(confd);//处理请求
		exit(0);
	}
	close(confd);
}

2、accept + thread, TCP并发服务器程序
每个客户一个线程

此类型并发服务器的主体循环伪代码如下

pthread_t tid;
for(;;){
	clilen = addrlen;
	if((confd = accept(listenfd,cliaddr,&clilem)) < 0)
	{
		if(errno == EINTR)
			continue;
		else
			err_sys("accept error");
	}
	pthread_create(&tid,NULL,&doit,(void *)confd);	
}

void * doit(void * arg)
{
	void web_thread(int);
	pthread_detach(pthread_self());
	web_thread((int)arg);
	close((int)arg);
	return(NULL);

pthread_detach 函数将子线程与主线程分离,使得主线程不必等待子线程。

3、线程池, TCP并发服务器程序
在程序启动之后只让主线程调用accept,并把每个客户连接传递给池中的某个可用线程。
问题在于如何将一个已连接套接字传递给线程池中的某个可用的线程。我们原本可以使用描述符传递,但是既然所有的描述符和所有线程都在一个进程内,没有必要将一个描述符从一个线程传递到另外一个线程。接收线程只需要主动这个已连接套接字描述符的值,而描述符传递实际传递的并非是这值,而是这个套接字的一个引用,因而将返回一个不同于原值的描述符(但是套接字的引用计数增加)。

此类型并发服务器的主体循环伪代码如下

int nthread;
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITTIALIZER;
/
创建线程池中的每一个线程
void thread_make(int i)
{
	void *thread_main(void*);
	pthread_create(&tptr[i].thread_tid,NULL,&thread_main,(void *)i);
	return;//主线程返回
}


//线程池中的每个线程都试图获取保护clifd数组的互斥锁,获得之后测试input和iget,若两者相等,表示无事可做,
//于是通过pthread_cond_wait睡眠在条件变量上。主线程接收一个连接之后会调用pthread_cond_signal 向条件变量发送信号,
//以唤醒睡眠在其上的线程。若input与iget不相等,则从clifd数组中取出下一个元素获取一个连接,然后调用web_child
void thread_main(void * arg)
{
	int confd;
	void web_thread(int);
	printf("thread %d strating\n",(int)arg);
	for(;;)
	{
		pthread_mutex_lock(&clifd_mutex);
		while(iget == input)  
			pthread_cond_wait(&clifd_cond,&clifd_mutex);
		confd = clifd[iget];
		if(++iget == MAXNCLI)
			iget = 0;
		pthread_mutex_unlock(&clifd_mutex);
		tptr[(int)arg].thread_count++;
		web_child(confd);
		close(confd);
	}
}
//
for(i = 0;i<nthread;++i)
	thread_make(i);//只有主线程返回,创建线程池中的每一个线程
for(;;){
	clilen = addrlen;
	if((confd = accept(listenfd,cliaddr,&clilem)) < 0)
	{
		if(errno == EINTR)
			continue;
		else
			err_sys("accept error");
	}
	pthread_mutex_lock(&clifd_mutex);	
	clifd[input] = confd;//clifd[]数组,由主线程往中存入已接受的已连接套接字描述符,
	                    //并由线程池中的可用线程从中取出一个来服务相应的客户
	if(++input == MAXNCLI)  //input 是主线程将往该数组clifd[]中存入的下一个元素的下标
		input = 0;
	if(input == iget) //iget表示线程池中某个线程将从该数组clifd[]中取出的下一个元素的下标。
		err_quit("input = iget =  %d",input);
	pthread_cond_signal(&clifd_cond);\
	pthread_mutex_unlock(&clifd_mutex);
}

4、Reactor + thread-per-task, TCP并发服务器程序

服务器收到请求之后,创建一个新的线程取处理请求,以充分利用多核CPU。

5、Reactors in threads, TCP并发服务器程序

此方案的特定是one loop per thread, 有一个main reactor 负责accept 连接,然后将连接挂在某个sub reactor 中,这样该连接的所有操作都在那个 sub reactor 所处的线程中完成。
多个连接可能被分配到多个线程,充分利用CPU。
在这里插入图片描述

分类 网络编程 学习笔记

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值