signal(SIGPIPE,SIG_IGN);
?? ?char pwd_path[256]="";记录工作目录
?? ?char * path = getenv("PWD");获取当前目录工作路径
?? ?///home/itheima/share/bjc++34/07day/web-http
?? ?strcpy(pwd_path,path);字符串复制函数
?? ?strcat(pwd_path,"/web-http");两个字符型连接起来
?? ?chdir(pwd_path);改变当前工作路径
int lfd = tcp4bind(PORT,NULL);创建lfd,并且绑定,
Listen(lfd,128);监听
int epfd = epoll_create(1);创建红黑树,创建句柄
struct epoll_event ev,evs[1024];创建事件结构体和事件集合
?ev.data.fd = lfd;结构体内部的文件描述
?ev.events = EPOLLIN;定义事件为读事件
?epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);将lfd上树,
while(1)循环监听
?? ?{
?? ??? ?int nready = epoll_wait(epfd,evs,1024,-1);去监听集合
?? ??? ?if(nready < 0)报错
?? ??? ?{
?? ??? ??? ?perror("");
?? ??? ??? ?break;
?? ??? ?}
?? ??? ?else我们需要查找集合那个发生变化
?? ??? ?{
?? ??? ??? ?for(int i=0;i<nready;i++)
?? ??? ??? ?{
?? ??? ??? ??? ?printf("001\n");
?? ??? ??? ??? ?if(evs[i].data.fd == lfd && evs[i].events & EPOLLIN)判断是否是lfd,且是读事件
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?struct sockaddr_in cliaddr;构建客户端地址结构体
?? ??? ??? ??? ??? ?char ip[16]="";ip地址接收
?? ??? ??? ??? ??? ?socklen_t len = sizeof(cliaddr);地址长度
?? ??? ??? ??? ??? ?int cfd = Accept(lfd,(struct sockaddr*)&cliaddr,&len);提取新的cfd
?? ??? ??? ??? ??? ?printf("new client ip=%s port=%d\n",
?? ??? ??? ??? ??? ??? ?inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,ip,16),
?? ??? ??? ??? ??? ??? ?ntohs(cliaddr.sin_port));并且打印出出ip和端口
?? ??? ??? ??? ??? ?int flag = fcntl(cfd,F_GETFL);需要设置cfd为非阻塞fcntl函数可以设置
?? ??? ??? ??? ??? ?flag |= O_NONBLOCK;设置标志位
?? ??? ??? ??? ??? ?fcntl(cfd,F_SETFL,flag);
?? ??? ??? ??? ??? ?ev.data.fd = cfd;将cfd上树
?? ??? ??? ??? ??? ?ev.events = EPOLLIN;事件是读事件
?? ??? ??? ??? ??? ?epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
?? ??? ??? ??? ?}
?? ??? ??? ??? ?else if(evs[i].events & EPOLLIN)//cfd变化
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?read_client_request(epfd,&evs[i]);我们去调用这个函数读客户端请求
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?}
?? ?}
void read_client_request(int epfd ,struct epoll_event *ev)//句柄和事件
{
char buf[1024]="";//读取一行,再把其他行读取,扔掉
char tmp[1024]="";//
int n = Readline(ev->data.fd, buf, sizeof(buf));
if(n <= 0)//读取的是小于等于0代表关闭或者出错
{
printf("close or err\n");
epoll_ctl(epfd,EPOLL_CTL_DEL,ev->data.fd,ev);下树
close(ev->data.fd);关闭文件描述符
return ;
}
printf("[%s]\n", buf);缓冲区数据打印出来
int ret =0;返回值接收
while( (ret = Readline(ev->data.fd, tmp, sizeof(tmp))) >0);
//解析请求行 GET /a.txt HTTP/1.1\R\N
//
char method[256]="";get
char content[256]="";/a.txt
char protocol[256]="";HTTP/1.1\R\N
sscanf(buf,"%[^ ] %[^ ] %[^ \r\n]",method,content,protocol);拆字符串空格拆分,
printf("[%s] [%s] [%s]\n",method,content,protocol );打印出来
//判断是否为get请求 get GET
if( strcasecmp(method,"get") == 0)
{
//[GET] [/%E8%8B%A6%E7%93%9C.txt] [HTTP/1.1]拆解的字符
char *strfile = content+1;
strdecode(strfile,strfile);
//GET / HTTP/1.1\R\N
if(*strfile == 0)//如果没有请求文件,默认当前目录
strfile= "./";
//判断请求的文件在不在
struct stat s;
if(stat(strfile,&s)< 0)//文件不存在
{
printf("file not fount\n");
//先发送 报头(状态行 消息头 空行)
send_header(ev->data.fd, 404,"NOT FOUND",get_mime_type("*.html"),0);调用
//发送文件error.html
send_file(ev->data.fd,"error.html",ev,epfd,1);调用
}
else
{
//请求一个普通文件
if(S_ISREG(s.st_mode))
{
printf("file\n");
//先发送 报头(状态行 消息头 空行)
send_header(ev->data.fd, 200,"OK",get_mime_type(strfile),s.st_size);
//发送文件
send_file(ev->data.fd,strfile,ev,epfd,1);
}
else if(S_ISDIR(s.st_mode))//请求是一个目录
{
printf("dir\n");
//发送一个列表 网页
send_header(ev->data.fd, 200,"OK",get_mime_type("*.html"),0);
//发送header.html
send_file(ev->data.fd,"dir_header.html",ev,epfd,0);
struct dirent **mylist=NULL;定义一个指针
char buf[1024]="";缓冲区
int len =0;长度
int n = scandir(strfile,&mylist,NULL,alphasort);
for(int i=0;i<n;i++)
{
//printf("%s\n", mylist[i]->d_name);
if(mylist[i]->d_type == DT_DIR)//如果是目录
{
len = sprintf(buf,"<li><a href=%s/ >%s</a></li>",mylist[i]->d_name,mylist[i]->d_name);
}
else
{
len = sprintf(buf,"<li><a href=%s >%s</a></li>",mylist[i]->d_name,mylist[i]->d_name);
}
send(ev->data.fd,buf,len ,0);发送数据
free(mylist[i]);释放
}
free(mylist);释放
send_file(ev->data.fd,"dir_tail.html",ev,epfd,1);发送文件
}
}
}
}
void send_header(int cfd, int code,char *info,char *filetype,int length)
{ 发送状态行
char buf[1024]="";缓冲区
int len =0;
len = sprintf(buf,"HTTP/1.1 %d %s\r\n",code,info);字符串格式化
send(cfd,buf,len,0);发送cfd中
//发送消息头
len = sprintf(buf,"Content-Type:%s\r\n",filetype);格式化
send(cfd,buf,len,0);发送到cfd中
if(length > 0)
{
//发送消息头
len = sprintf(buf,"Content-Length:%d\r\n",length);对长度处理
send(cfd,buf,len,0);
}
//空行
send(cfd,"\r\n",2,0);
}
4.发送文件send_file函数
void send_file(int cfd,char *path,struct epoll_event *ev,int epfd,int flag)
{
int fd = open(path,O_RDONLY);只读打开路径,生成文件描述符
if(fd <0)文件不存在报错
{
perror("");
return ;
}
char buf[1024]="";定义一个缓冲区
int len =0;长度
while( 1)
{
len = read(fd,buf,sizeof(buf));从缓冲区读
if(len < 0)如果没有报错
{
perror("");
break;
}等于0跳出
else if(len == 0)
{
break;
}
else
{
int n=0;
n = send(cfd,buf,len,0);我们发送数据
printf("len=%d\n", n);
}
}
close(fd);关闭文件描述符
//关闭cfd,下树
if(flag==1)
{
close(cfd);
epoll_ctl(epfd,EPOLL_CTL_DEL,cfd,ev);
}
}
#define PORT 8889端口号,
上述就是实现webserver整体代码了