预先创建线程之由主线程分发连接请求
此种方式是由主线程预先创建多个工作线程,并且主线程不断的监听连接请求,当有连接请求时先获得锁,并将连接放入到连接数组中,并通知空闲的工作线程(即阻塞在条件等待处的工作线程)进行请求处理,工作线程得到通知后先在连接数组中拿一个可用的连接,并将其标记为已处理,然后释放锁,执行请求处理函数。此方式处理的开销要比前一种的方式大,因为要工作线程获得一个连接要同时处理锁及条件变量,而前一种方式只需要维护一个锁即可。
例子:回射服务器,即服务器端接收客户端来的数据,并将数据原样返回给客户端,代码在CentOS5.0测试通过
服务器端代码:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/select.h>
#include <pthread.h>
#include <stdlib.h>
#define SERV_PORT 2003
#define MAXLINE 1024
#define LISTENQ 4
#define CLIENTNUM 5
#define MAXNCLI 20
int listenfd;
pthread_t *child;
pthread_mutex_t mutex;
pthread_cond_t cond;
int clifd[MAXNCLI], iput, iget;
/* 客户请求处理函数 */
void requestProcessFunc(int connfd)
{
char buf[MAXLINE];
int n;
for(;;) {
if((n = read(connfd, buf, MAXLINE)) == 0) {
printf("client is quit\n");
break;
}else{
write(connfd, buf, n);
}
}
}
int makeListenByPort(short port)
{
int listenfd;
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(port);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
return listenfd;
}
void * threadMain(void *arg)
{
int connfd;
pthread_mutex_lock(&mutex);
/* 如果连接数组中没有有效的连接,即进入阻塞状态 */
while(iget == iput)
pthread_cond_wait(&cond, &mutex);
/* 当条件被激活,工作线程从连接数据中拿到一个有效连接,移动有效连接的下标 */
connfd = clifd[iget++];
if(iget == MAXNCLI)
iget = 0;
pthread_mutex_unlock(&mutex);
/* 调用请求处理函数 */
requestProcessFunc(connfd);
close(connfd);
}
void makeThread(int n)
{
pthread_create(&child[n],NULL, threadMain, (void *)n);
return;
}
int main(intargc, char **argv)
{
int i;
socklen_t tmpAddLen, addLen;
int connfd;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
addLen = sizeof(struct sockaddr_in);
listenfd = makeListenByPort(SERV_PORT);
child = (pthread_t*)malloc(sizeof(pthread_t) * CLIENTNUM);
if(child == NULL) {
printf("memory mallocerror!");
exit(0);
}
/* 创建工作线程 */
for(i = 0; i < CLIENTNUM; i++) {
makeThread(i);
}
iget = iput = 0;
for(;;) {
tmpAddLen = addLen;
connfd = accept(listenfd, NULL,&tmpAddLen);
pthread_mutex_lock(&mutex);
clifd[iput++] = connfd;
if(iput == MAXNCLI)
iput = 0;
/* 通知工作线程已经有连接请求 */
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#define MAXLINE 1024
#define SERV_PORT 2003
void cli_str(FILE *fp, int sockfd)
{
char sendline[MAXLINE];
char recvline[MAXLINE];
memset(recvline, 0x00, sizeof(recvline));
memset(sendline, 0x00, sizeof(sendline));
printf("cli#");
while((fgets(sendline, MAXLINE, fp)) != NULL) {
write(sockfd, sendline, strlen(sendline));
if(read(sockfd, recvline, MAXLINE) == 0) {
printf("str_cli:server terminated prematurely\n");
exit(0);
}
fputs(recvline, stdout);
fflush(stdout);
memset(recvline, 0x00, sizeof(recvline));
printf("cli#");
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("socket error\n");
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
if((connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))== -1) {
printf("connect error\n");
exit(0);
}
cli_str(stdin, sockfd);
exit(0);
}