基于EPOLL写的HTTP服务器(加入了线程池)_没落都城_新浪博客
基于EPOLL写的HTTP服务器(加入了线程池)
(2010-12-07 19:02:51)
转载▼
标签:
杂谈
分类: EPOLL
#include<fcntl.h>
#include<cstdio>
#include<unistd.h>
#include<cstdlib>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<cstring>
#include<pthread.h>
const int EPOLL_SIZE=5000;
const int EVENT_ARR=5000;
const int PORT=8002;
const int BUF_SIZE=5000;
const int BACK_QUEUE=100;
const int THREAD_MAX=100;
static unsigned int s_thread_para[THREAD_MAX][8]; //线程参数
static pthread_t s_tid[THREAD_MAX]; //线程ID
pthread_mutex_t s_mutex[THREAD_MAX]; //线程锁
int epFd; //epoll
struct epoll_event ev,evs[EVENT_ARR];
char *get_type(char *url,char *buf)
{
const char *t=url+strlen(url);
char type[64];
for(;t>=url&&*t!='.';--t) ;
strcpy(type,t+1);
if(strcmp(type,"html")==0||strcmp(type,"htm")==0)
sprintf(buf,"text/%s",type);
else if(strcmp(type,"gif")==0||
strcmp(type,"jpg")==0||
strcmp(type,"jpeg")==0||
strcmp(type,"png")==0)
sprintf(buf,"image/%s",type);
else if(strcmp(type,"/")==0)
sprintf(buf,"text/html");
else if(strcmp(type,"css")==0)
sprintf(buf,"text/css");
else if(strcmp(type,"js")==0)
sprintf(buf,"application/x-javascript");
else
{
sprintf( buf, "unknown" );
}
return buf;
}
void* http_server(int thread_para[])
{
int pool_index; //thread pool ID
int clientFd; //client socket
char buf[BUF_SIZE];
pthread_detach(pthread_self());
pool_index=thread_para[7];
wait_unlock:
pthread_mutex_lock(s_mutex+pool_index); //wait for thread unlock
clientFd=thread_para[1]; //client socket ID
//先进行试探性读取
int len=read(clientFd,buf,BUF_SIZE);
printf("%s",buf);
if(len>0)
{
char *token=strtok(buf," "); //GET
printf("token:%s",token);
char type[64];
char *url=strtok(NULL," "); //URL
while(*url=='.'||*url=='/')++url;
printf("url:%s",url);
char file[1280000];
sprintf(file,"%s",url);
printf("file:%s",file);
FILE *fp=fopen(file,"rb");
if(fp==0)
{
char response[]="HTTP/1.1 404 NOT FOUND\r\n\r\n";
printf("HTTP/1.1 404 NOT FOUND\r\n\r\n");
write(clientFd,response,strlen(response));
}
else
{
int file_size;
char *content;
char *response;
fseek(fp,0,SEEK_END);
file_size=ftell(fp);
fseek(fp,0,SEEK_SET);
content=(char *)malloc(file_size+1);
response=(char*)malloc(200);
fread(content,file_size,1,fp);
content[file_size]=0;
sprintf(response,"HTTP/1.1 200 OK\r\nContent-Length:%d\r\nContent-Type:%s\r\n\r\n",file_size,get_type(url,type));
// printf("HTTP/1.1 200 OK\r\nContent-Type:%s\r\nContent-Length:%d\r\n\r\n%s",get_type(url,type),file_size,content);
write(clientFd,response,strlen(response));
write(clientFd,content,file_size);
free(content);
free(response);
}
}
else if(len==0)
{
//触发了EPOLL事件,却没有读取,表示断线
//printf("Client closed at %s\n",inet_ntoa(clientAddr.sin_addr));
epoll_ctl(epFd,EPOLL_CTL_DEL,clientFd,&ev);
close(clientFd);
int i=thread_para[3];
evs[i].data.fd=-1;
}
else if(len==EAGAIN)
{
printf("socket huan cun man le!\n");
}
else
{
//client读取出错
printf("Client read failed!\n");
}
thread_para[0] = 0;//设置线程占用标志为"空闲"
goto wait_unlock;
printf("pthread exit!\n");
pthread_exit(NULL);
}
static int init_thread_pool(void)
{
int i,rc;
for(i=0;i<THREAD_MAX;i++)
{
s_thread_para[i][0]=0; //idle
s_thread_para[i][7]=i; //thread pool ID
pthread_mutex_lock(s_mutex+i); //thread lock
}
// create thread pool
for(i=0;i<THREAD_MAX;i++)
{ rc=pthread_create(s_tid+i,0,(void *(*)(void*))&http_server,(void *)(s_thread_para[i]));
if(0!=rc)
{ fprintf(stderr,"Create thread failed!\n");
return -1;
}
}
return 0;
}
void setnoblock(int sockFd) //设置非阻塞模式
{
int opt;
if((opt=fcntl(sockFd,F_GETFL))<0) //获取原来的flag;
{
printf("GET FL failed!\n");
exit(-1);
}
opt|=O_NONBLOCK;
if(fcntl(sockFd,F_SETFL,opt)<0)
printf("SET FL failed!\n");
}
int main()
{
int serverFd,j;
serverFd=socket(AF_INET,SOCK_STREAM,0); //创建服务器fd
setnoblock(serverFd); //设置为非阻塞模式
unsigned int optval;
struct linger optval1;
//设置SO_REUSEADDR选项(服务器快速重起)
optval = 0x1;
setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &optval, 4);
// 设置SO_LINGER选项(防范CLOSE_WAIT挂住所有套接字)
optval1.l_onoff = 1;
optval1.l_linger = 60;
setsockopt(serverFd, SOL_SOCKET, SO_LINGER, &optval1, sizeof(struct linger));
//创建epoll,并将serverFd放入监听队列
epFd=epoll_create(EPOLL_SIZE);
ev.data.fd=serverFd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epFd,EPOLL_CTL_ADD,serverFd,&ev);
//绑定服务器端口
struct sockaddr_in serverAddr;
socklen_t serverlen=sizeof(struct sockaddr_in);
serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
serverAddr.sin_port=htons(PORT);
if(bind(serverFd,(struct sockaddr*)&serverAddr,serverlen))
{
printf("BIND failed!\n");
exit(-1);
}
//线程池初始化
int rc = init_thread_pool();
if (0 != rc) exit(-1);
//打开监听
if(listen(serverFd,BACK_QUEUE))
{
printf("Listen failed!\n");
exit(-1);
}
//服务处理
int clientFd;
sockaddr_in clientAddr;
socklen_t clientlen;
for(;;)
{
//等待epoll事件到来,最多取EVENT_ARR个事件
int nfds=epoll_wait(epFd,evs,EVENT_ARR,-1);
//处理事件
for(int i=0;i<nfds;i++)
{
if(evs[i].data.fd==serverFd&&evs[i].events&EPOLLIN)
{
//如果是serverFd,表明有新连接连入
if((clientFd=accept(serverFd,(struct sockaddr*)&clientAddr,&clientlen))<0)
{
printf("ACCEPT failed\n");
}
printf("Connect from %s:%d\n",inet_ntoa(clientAddr.sin_addr),htons(clientAddr.sin_port));
setnoblock(clientFd);
//注册accept()到的连接
ev.data.fd=clientFd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epFd,EPOLL_CTL_ADD,clientFd,&ev);
}
else if(evs[i].events&EPOLLIN)
{
//如果不是serverFd,则是client的可读
printf("client can write!\n");
if((clientFd=evs[i].data.fd)>0)
{
//查询空闲线程对
for(j = 0; j < THREAD_MAX; j++) {
if (0 == s_thread_para[j][0]) break;
}
if (j >= THREAD_MAX) {
fprintf(stderr, "线程池已满, 连接将被放弃\r\n");
shutdown(clientFd, SHUT_RDWR);
close(clientFd);
continue;
}
//复制有关参数
s_thread_para[j][0] = 1;//设置活动标志为"活动"
s_thread_para[j][1] = clientFd;//客户端连接
s_thread_para[j][2] = serverFd;//服务索引
s_thread_para[j][3]=i; //epoll event id;
//线程解锁
pthread_mutex_unlock(s_mutex + j); }
else printf("other error!\n");
}
}
}
return 0;
}