参考《linux高性能服务器编程》
前提:实现一个锁类和一个线程池类
#include <pthread.h>
#include <semaphore.h>
class locker{
private:
pthread_mutex_t mutex;
public:
locker()
{
pthread_mutex_init(&mutex,NULL);
}
~locker()
{
pthread_mutex_destroy(&mutex);
}
bool lock()
{
return pthread_mutex_lock(&mutex)==0;
}
bool unlock()
{
return pthread_mutex_unlock(&mutex)==0;
}
};
class sem{
private:
sem_t m_sem;
public:
sem()
{
sem_init(&m_sem,0,0);
}
~sem()
{
sem_destroy(&m_sem);
}
bool wait()
{
return sem_wait(&m_sem);
}
bool post()
{
return sem_post(&m_sem);
}
};
#include <pthread.h>
#include <algorithm>
#include <iostream>
#include <stdbool.h>
#include<list>
#include"locker.h"
using namespace std;
template<typename T>
class threadpool{
private:
list<T*>Task;
locker list_lock;
sem list_sem;
public:
threadpool()
{
this->createthread();
}
bool isempty()
{
return this->Task.empty();
}
void createthread()
{
pthread_t id;
for(int i=0;i<8;i++)
{
pthread_create(&id,NULL,runthread,this);
}
}
T* poptask()
{
list_lock.lock();
T *front=Task.front();
Task.pop_front();
list_lock.unlock();
return front;
}void pushback(T* task)
{
list_lock.lock();
Task.push_back(task);
cout<<"此时任务队列个数:"<<Task.size()<<endl;
list_lock.unlock();
list_sem.post();
}
private:
static void *runthread(void *poll)
{
threadpool *p=(threadpool*)poll;
pthread_detach(pthread_self());
while(1)
{
p->list_sem.wait();
T *task=p->poptask();
cout<<"线程"<<pthread_self()<<"取得任务"<<endl;
task->work();
}
}
};
线程池实现思想:(模版类可以放入任意种类工作类型),在构造函数中创建8个子线程将它们全部阻塞等待信号量的释放,一旦有工作被压入工作队列就会有线程通过锁来占用工作队列并取出工作进行作业。
具体讲讲工作类型如何实现
每一个工作都代表着一个客户对应的文件描述符,具体关注work()函数,work()函数中第一个循环用于ET模式下的epoll监听(必须循环读取客户端发来的信息,epoll不会重复提醒是否有数据可以读取);第二个循环用于启动状态机,最后会返回分析客户端请求的结果,将结果进行匹配,如果是GET请求就发送给客户端一个html文本。
状态机的实现比较复杂可以参考《Linux高性能服务器编程的8.6章》
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/epoll.h>
int setnonblocking(int fd)
{
int old_option=fcntl(fd,F_GETFL);
int new_option=old_option | O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
}
void addfd(int epollfd,int fd)
{
epoll_event event;
event.data.fd=fd;
event.events=EPOLLIN | EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblocking(fd);
}
void removefd(int epollfd,int fd)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,NULL);
close(fd);
}
class basetask{
private:
int connfd;
public:
basetask(){}
void init(int sockfd)
{
connfd=sockfd;
}
void work()
{
char buff[1024];
memset(buff,'\0',sizeof(buff));
int start_index=0,total_index=0,startlines=0;
CHECK_STATE checkstate=CHECK_STATE_REQUSTLINE;//初始状态机
while(1)
{
int revc_size=recv(connfd,buff,sizeof(buff),0);
if(revc_size>0)
{
total_index+=revc_size;
printf("%s\n",buff);
}
else if(revc_size==0)
{
printf("连接关闭\n");
break;
}
else
{
break;
}
}//接受到的请求字符串
while(1)//启动状态机
{
HTTP_CODE result=enter(buff,start_index,checkstate,total_index,startlines);
if(result==NO_REQUST)
{
continue;
}
else if(result==GET_QUEST)
{
/*发送html文件*/
int p=open("xia.html",O_RDONLY);
char myhtml[1024];
strcpy(myhtml,"HTTP/1.0 200 OK\r\n");
strcat(myhtml,"Server: xiakaiwei\r\n");
strcat(myhtml,"Content-type: text/html\r\n");
strcat(myhtml,"Connection: close\r\n");
char text[1024]={0};
int read_size=read(p,text,sizeof(text)-1);
char newbuff[1024];
sprintf(newbuff,"Content-Length: %d\r\n\r\n",read_size);
strcat(myhtml,newbuff);
strcat(myhtml,text);
write(connfd,myhtml,sizeof(myhtml));
printf("html文本发送完毕\n");
close(connfd);
}
else
{
printf("请求错误\n");
break;
}
}
}
enum CHECK_STATE{CHECK_STATE_REQUSTLINE,CHECK_STATE_HEADER};//主状态机,分析请求行和请求头部
enum LINE_STATE{LINE_OK,LINE_BAD,LINE_OPEN};//读取行的状态,完整的行,行出错,行数据不完整
enum HTTP_CODE{NO_REQUST,GET_QUEST,BAD_QUEST,FORBIDDEN_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};//请求不完整,需要继续读取;获得完整的请求;请求有语法错误;客户没有访问权限;服务器出错;客户端关闭连接
LINE_STATE read_line(char *buffer,int &start_index,int total_index)
{
/*start_index指向需要读取的第一个字符处total_index指向所有字符后的下一个字符处*/
char ch;
for(;start_index<total_index;start_index++)
{
ch=buffer[start_index];
if(ch=='\r')
{
if(start_index+1==total_index)//如果'\r'是最后一个数据则返回行数据不完整
{
return LINE_OPEN;
}
else if(buffer[start_index+1]=='\n')
{
buffer[start_index++]='\0';
buffer[start_index++]='\0';
return LINE_OK;//将\r\n处置为空字符,start_index指向\n的下一个字符
}
return LINE_BAD;
}
else if(ch=='\n')
{
if(start_index>1&&buffer[start_index-1]=='\r')
{
buffer[start_index-1]='\0';
buffer[start_index++]='\0';
return LINE_OK;
}
return LINE_BAD;
}
}
return LINE_OPEN;//所有内容分析完后都没遇到\r则返回行数据不完整
}
HTTP_CODE requestline(char *temp,CHECK_STATE &checkstate)
{
char *url=strpbrk(temp," \t");//url指向GET后的空格
if(!url)
{
return BAD_QUEST;
}
*url++='\0';
char *method=temp;
if(strcasecmp(method,"GET")==0)
{
printf("请求方法是GET\n");
}
else
{
return BAD_QUEST;
}
url+=strspn(url," \t");//如果是\turl指向请求url的第一个字符,如果是空格则url不变
char *version=strpbrk(url," \t");
if(!version)
{
return BAD_QUEST;
}
*version++='\0';
version+=strspn(version," \t");
if(strcasecmp(version,"HTTP/1.1")!=0)
{
return BAD_QUEST;
}
if(strncasecmp(url,"http://",7)==0)
{
url+=7;
url=strchr(url,'/');//url指向/
}
if(!url || url[0]!='/')
{
return BAD_QUEST;
}
printf("请求url是%s\n",url);
checkstate=CHECK_STATE_HEADER;
return NO_REQUST;
}
HTTP_CODE requestheader(char *temp)
{
if(temp[0]=='\0')
{
return GET_QUEST;
}
else if(strncasecmp(temp,"Host:",5)==0)
{
temp+=5;
temp+=strspn(temp," \t");//Host:后面有空格或者\t
printf("Host:%s\n",temp);
}
else
{
//其它字段不处理
}
return NO_REQUST;
}
HTTP_CODE enter(char *buffer,int &start_index,CHECK_STATE &checkstate,int &total_index,int &startlines)
{
LINE_STATE linestatus=LINE_OK;
HTTP_CODE code=NO_REQUST;
while((linestatus=read_line(buffer,start_index,total_index))==LINE_OK)
{
char *temp=buffer+startlines;
startlines=start_index;
switch(checkstate)
{
case CHECK_STATE_REQUSTLINE:
{
code=requestline(temp,checkstate);
if(code==BAD_QUEST)
{
return BAD_QUEST;
}
break;
}
case CHECK_STATE_HEADER:
{
code=requestheader(temp);
if(code==BAD_QUEST)
{
return BAD_QUEST;
}
else if(code==GET_QUEST)
{
return GET_QUEST;
}
break;
}
default:
{
return INTERNAL_ERROR;
}
}
}
if(linestatus==LINE_OPEN)
{
return NO_REQUST;
}
else
{
return BAD_QUEST;
}
}
};