预先创建线程之accept之前获得锁
预先创建线程是由一个主线程创建多个工作线程,然后工作线程通过互斥机制处理每个客户的连接。有两种常见的方法可以实现,一种是工作线程调用accept,并在调用accept加入锁保护。第二种是主线程调用accept函数,当一个客户端连接服务器后,主线程通知工作线程处理客户请求。这两种实现方法将分进行说明。
在第一种方式中,工作线程调用accept之前就必须获得锁,获得锁的工作线程阻塞在accept函数处,当有连接请求时accept函数返回,然后释放掉锁,并调用请求处理函数进行处理,其它在等锁的工作线程中的一个获得锁被阻塞致accept函数等待连接请求。当工作线程处理完请求处继续等锁。
例子:回射服务器,即服务器端接收客户端来的数据,并将数据原样返回给客户端,代码在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
int listenfd;
pthread_t *child;
pthread_mutex_t mutex;
socklen_t addLen;
/* 客户请求处理函数 */
void requestProcessFunc(int connfd)
{
char buf[MAXLINE];
int n;
for(;;) {
if((n = read(connfd,buf, MAXLINE)) == 0) {
printf("clientis 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)
{
socklen_t tmpAddLen;
int connfd;
tmpAddLen = addLen;
for(;;) {
/* accept之前先获得锁 */
pthread_mutex_lock(&mutex);
connfd = accept(listenfd, NULL, &tmpAddLen);
if(connfd <= 0) {
printf("accept funcerror!");
exit(0);
}
/* 返回后马上释放锁 */
pthread_mutex_unlock(&mutex);
/* 调用请求处理函数 */
requestProcessFunc(connfd);
close(connfd);
}
}
/* 创建工作线程 */
void makeThread(int n)
{
pthread_create(&child[n], NULL, threadMain, NULL);
return;
}
int main(int argc, char **argv)
{
int i;
pthread_mutex_init(&mutex, NULL);
addLen = sizeof(struct sockaddr_in);
listenfd = makeListenByPort(SERV_PORT);
child = (pthread_t *)malloc(sizeof(pthread_t) * CLIENTNUM);
if(child == NULL) {
printf("memory malloc error!");
exit(0);
}
for(i = 0; i < CLIENTNUM; i++) {
makeThread(i);
}
for(;;) pause();
}
客户端代码:
#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_inservaddr;
if((sockfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("socketerror\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("connecterror\n");
exit(0);
}
cli_str(stdin, sockfd);
exit(0);
}