日期:2014-05-16  浏览次数:20682 次

UNIX TCP回射服务器/客户端(6):线程池服务器

    《Unix网络编程》这本书附带了许多短小精美的小程序,我在阅读此书的时候,将书上的代码按照自己的理解重写了一遍(大部分是抄书上的),加深一下自己的理解(纯看书太困了,呵呵)。此例子在Ubuntu10.04上测试通过。

    PS:程序里使用了包裹函数(首字母是大写的函数)和常量(所有字母都是大写的常量)的声明在my_unp.h文件中,定义在unp_base.c和unp_thread.c中,地址:http://blog.csdn.net/aaa20090987/article/details/8096701

    程序简介:这是一个基本的线程池服务器模型。服务器启动时,先启动N个用于处理与客户端通信的子线程。当一个客户端连接上服务器时(accept),各个空闲的子线程都会去accept这个连接,但因为互斥锁的关系,总能保证有且只有一个线程会accept成功。然后这个子线程就会与客户端进行通信。


上代码:

#include "my_unp.h"

typedef struct 
{
	pthread_t thread_tid;	//线程ID
	long thread_count;	 //线程处理的条数
} Thread;

Thread *tptr;
int    listenfd, nthreads;
socklen_t  addrlen;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

//接受来自客户端字符串,然后再原样返回
void str_echo(int sockfd)
{
	ssize_t n;  
	char buf[MAXLINE];  

again:  
	//从套接字中读取数据,写到buffer中去  
	//再将buffer中的数据写到套接字中去  
	while( (n=read(sockfd, buf, MAXLINE)) > 0 )  
		Writen(sockfd, buf, n); 

	//由于信号中断,没写或读成功任何数据  
	if( n<0 && errno==EINTR )  
		goto again;  
	else if( n < 0 )  
		error_quit("str_echo: read error");  
}

void *thread_main(void *arg)
{
	int    connfd;
	socklen_t  clilen;
	struct sockaddr *cliaddr;

	cliaddr = Malloc(addrlen);

	printf("thread %d starting\n", (int) arg);
	while(1)
	{
		//当请求到达时,加锁,接收,解锁,处理数据
		//这样就保证了每个请求只能被一个随机线程所处理
		clilen = addrlen;
		Pthread_mutex_lock(&lock);
		connfd = Accept(listenfd, cliaddr, &clilen);
		Pthread_mutex_unlock(&lock);
		tptr[ (int)arg ].thread_count++;
		str_echo(connfd);
		Close(connfd);
	}
}

int main(int argc, char **argv)
{
	int  i;
	struct sockaddr_in servaddr; 
	if( argc != 2 )
		error_quit("Using: server <#thread num>");

	//创建用于TCP协议的套接字   
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);  
	memset(&servaddr, 0, sizeof(servaddr));  
	servaddr.sin_family = AF_INET;  
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
	servaddr.sin_port = htons(SERV_PORT);  

	//把socket和socket地址结构联系起来    
	Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));  
	//开始监听LISTENQ端口  
	Listen(listenfd, LISTENQ);  

	nthreads = atoi(argv[1]);
	tptr = Calloc(nthreads, sizeof(Thread));

	//创建处理线程,并让主线程挂起
	for (i = 0; i < nthreads; i++)
	{
		Pthread_create(&tptr[i].thread_tid, NULL, 
			&thread_main, (void *)i);
	}
	pause();

	return 0;
}

与其配套的客户端在这里:http://blog.csdn.net/aaa20090987/article/details/8462262