实现效果如下:
图片可以直接显示
cpp h 这些可以直接显示 其他的 则是提示是否要下载
单线程 还有bug
代码如下 先放上来
#include "httpserver.h"
#include "stdio.h"
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <dirent.h>#define BURSIZE 1024
int hex2dec(char c)
{if ('0' <= c && c <= '9') {return c - '0';} else if ('a' <= c && c <= 'f') {return c - 'a' + 10;} else if ('A' <= c && c <= 'F') {return c - 'A' + 10;} else {return -1;}
}char dec2hex(short int c)
{if (0 <= c && c <= 9) {return c + '0';} else if (10 <= c && c <= 15) {return c + 'A' - 10;} else {return -1;}
}/** 编码一个url*/
void urlencode(char url[])
{int i = 0;int len = strlen(url);int res_len = 0;char res[BURSIZE];for (i = 0; i < len; ++i) {char c = url[i];if (('0' <= c && c <= '9') ||('a' <= c && c <= 'z') ||('A' <= c && c <= 'Z') || c == '/' || c == '.') {res[res_len++] = c;} else {int j = (short int)c;if (j < 0)j += 256;int i1, i0;i1 = j / 16;i0 = j - i1 * 16;res[res_len++] = '%';res[res_len++] = dec2hex(i1);res[res_len++] = dec2hex(i0);}}res[res_len] = '\0';strcpy(url, res);
}/** 解码url*/
void urldecode(char url[])
{int i = 0;int len = strlen(url);int res_len = 0;char res[BURSIZE];for (i = 0; i < len; ++i) {char c = url[i];if (c != '%') {res[res_len++] = c;} else {char c1 = url[++i];char c0 = url[++i];int num = 0;num = hex2dec(c1) * 16 + hex2dec(c0);res[res_len++] = num;}}res[res_len] = '\0';strcpy(url, res);
}int CreateSocketFD()
{int fd = 0;fd = socket(AF_INET,SOCK_STREAM,0);if(fd == -1){perror("Scoket fd = -1");return 0;}int reuseport = 1;int ret = setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuseport,sizeof(reuseport));if(ret == -1){perror("setsocketopt failed");return -1;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8888);addr.sin_addr.s_addr = INADDR_ANY;ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));if(ret == -1){perror("bind error");return -1;}ret = listen(fd,10);if(ret == -1){perror("listen error ");return -1;}return fd;
}int AcceptClients(int epoll_fd,int fd)
{struct sockaddr addr;int cfd = accept(fd,NULL,NULL);if(cfd == -1){perror("accept failed");}int flag = fcntl(cfd,F_GETFL);flag |= O_NONBLOCK;fcntl(cfd,F_SETFL,flag);struct epoll_event ev;ev.data.fd = cfd;ev.events = EPOLLIN|EPOLLET;int ret = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,cfd,&ev);if(ret == -1){perror("epoll ctl failed");return 0;}return 0;
}const char *GetFileType(const char *filename)
{const char *dot = strrchr(filename,'.');if(dot == NULL){return "text/plain; charset=utf-8";}if(strcmp(dot,".jpg") == 0 ||strcmp(dot,".jpeg") == 0){return "image/jpg";}if(strcmp(dot,".html") == 0 ||strcmp(dot,".htm") == 0){return "text/html; charset=utf-8";} if(strcmp(dot,".png") == 0){return "image/png";} if(strcmp(dot,".bmp") == 0){return "image/bmp";} if(strcmp(dot,".gif") == 0){return "image/gif";} if(strcmp(dot,".css") == 0){return "text/css";} if(strcmp(dot,".mp3") == 0){return "audio/mpeg";} return "text/plain; charset=utf-8";
}int SendHead(int cfd,int status ,const char *desc,const char *type,int size)
{char buf[4096] = {0};sprintf(buf,"http/1.1 %d %s\r\n",status,desc);sprintf(buf+strlen(buf),"content-type: %s\r\n",type);sprintf(buf+strlen(buf),"content-length: %d\r\n\r\n",size); printf("SendHead buf[%s]\n",buf);send(cfd,buf,strlen(buf),0);return 0;
}int SendDir(const char *dirname,int cfd)
{char buf[4096] = {0};sprintf(buf,"<html><head><title>%s</title></head><body><table>",dirname);printf("SendDir dirname=[%s]\n",dirname);struct dirent **namelist;int count = scandir(dirname,&namelist,NULL,alphasort);printf("SendDir count=[%d]\n",count);for(int i = 0;i< count;i++){char *name = namelist[i]->d_name;struct stat st;char sub_path[1024]={0};sprintf(sub_path,"%s/%s",dirname,name);stat(sub_path,&st);if(S_ISDIR(st.st_mode)){sprintf(buf+strlen(buf),"<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}else{sprintf(buf+strlen(buf),"<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}printf("cfd:%d Sendbuf[%s]\n",cfd,buf);send(cfd,buf,strlen(buf),0);memset(buf,0,sizeof(buf));free(namelist[i]);}sprintf(buf,"</table></body></html>");printf("cfd:%d Sendbuf[%s]\n",cfd,buf);send(cfd,buf,strlen(buf),0);free(namelist);return 0;
}int SendFile(const char* filename,int cfd)
{int fd = open(filename,O_RDONLY);if(fd >0){#if 0while(1){char buf[1024];int len = read(fd,buf,sizeof buf);if(len >0){send(cfd,buf,len,0);usleep(10);}else if(len == 0){printf("Read file end\n");break;}else{perror("read error");}}#elseoff_t offset = 0;int file_size = lseek(fd,0,SEEK_END);lseek(fd,0,SEEK_SET);while(offset <file_size){int send_len = sendfile(cfd,fd,&offset,file_size-offset);if(send_len == -1){if(errno == EAGAIN){//perror("sendfile no data send");}else{perror("sendfile ret -1");}}else{printf("Send len:%d\n",send_len);}}#endif}else{perror("open file failed");}close(fd);return 0;
}int ParseReqLine(const char *line,int cfd)
{char method[12];char path[1024];printf("ParseReqLine=[%s]\n",line);int ret = sscanf(line,"%[^ ] %[^ ]",method,path);printf("sscanf ret = %d\n",ret);printf("method=[%s],path=[%s]\n",method,path);urldecode(path);printf("afterdecode path=[%s]\n",path);if(ret ==2 ){}else{printf("Reqest line parse failed\n");return -1;}if(strcasecmp(method,"get") == 0){}else if(strcasecmp(method,"post")==0){}else{return -1;}char *file = NULL;if(strcmp(path,"/") == 0){ file = "./";}else{file = path+1;}struct stat st;ret = stat(file,&st);if(ret == -1){printf("file doest not exist\n");SendHead(cfd,404,"Not found",GetFileType(".html"),-1);SendFile("404.html",cfd);return -1;}if(S_ISDIR(st.st_mode)){printf("Directory\n");SendHead(cfd,200,"OK",GetFileType(".html"),-1);SendDir(file,cfd);}else{printf("File\n");SendHead(cfd,200,"OK",GetFileType(file),st.st_size);SendFile(file,cfd);}return 0;
}int Request(int epoll_fd,int cfd)
{char buffer[4096] = {0};char temp_buf[1024] = {0};int read_len = 0;int total = 0;while((read_len = recv(cfd,temp_buf,sizeof(temp_buf),0))>0){if(total+read_len <sizeof(buffer)){memcpy(buffer+total,temp_buf,read_len);total+=read_len;}}if(read_len == -1 && errno == EAGAIN){//读取数据结束char *p = strstr(buffer,"\r\n");if(p){int len = p - buffer;buffer[len] = 0;ParseReqLine(buffer,cfd);}}else if(read_len == 0){//Client close socketepoll_ctl(epoll_fd,EPOLL_CTL_DEL,cfd,NULL);close(cfd);}else{perror("recv error");}return 0;
}int EPOLL_Run(int server_fd)
{int epoll_fd = epoll_create(10);if(epoll_fd == -1){perror("epoll_create failed");return 0;}struct epoll_event ev;ev.data.fd = server_fd;ev.events = EPOLLIN;int ret = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,server_fd,&ev);if(ret == -1){perror("epoll_ctl failed");return 0;}struct epoll_event events[512];while(true){int nReady = epoll_wait(epoll_fd,events,512,-1);for(int i = 0;i<nReady;i++){int fd = events[i].data.fd;if(fd == server_fd){AcceptClients(epoll_fd,fd);}else{if(events[i].events &EPOLLOUT){//g_writeable = true;printf("客户端可以写数据了");}if(events[i].events &EPOLLIN){Request(epoll_fd,fd);}}}}return epoll_fd;
}
int main()
{printf("Hello world\n");char work_dir[] = "/home/develop/httpserver";//chdir(work_dir);int server_fd = CreateSocketFD();if(server_fd <=0){return 0;}EPOLL_Run(server_fd);close(server_fd);return 0;
}