实现效果如下:
图片可以直接显示
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 0
while(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");
}
}
#else
off_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 socket
epoll_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;
}
以上 如果遇到大文件 比如mp3 文件的话 就没办法 预览 试听 下载大文件也有问题
跟踪发现是SendFile 那里有问题 会返回-1
根据网上的例子 改了一个 基于libevent版本的 不会存在这个问题
#include "sushi.h"
#include "stdio.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <signal.h>
#include <fcntl.h>
#include <unordered_map>
#include <memory>
#include <vector>
#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>
#include <evhttp.h>
#include <event.h>
#include <string.h>
#include "event2/http.h"
#include "event2/event.h"
#include "event2/buffer.h"
#include "event2/bufferevent.h"
#include "event2/bufferevent_compat.h"
#include "event2/http_struct.h"
#include "event2/http_compat.h"
#include "event2/util.h"
#include "event2/listener.h"
#include "event2/thread.h"
#define MAX_EVENTS 100
#define RECVBUFSIZ 20
bool g_run_flag = true;
void sig_handler(int signo)
{
g_run_flag = false;
printf("\033[0;31mprogram exit by user cmd !!!!\033[0;39m\n");
}
#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);
}
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(struct bufferevent *event,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);
bufferevent_write(event,buf,strlen(buf));
return 0;
}
int SendDir(struct bufferevent *event,const char *dirname)
{
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);
bufferevent_write(event,buf,strlen(buf));
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);
bufferevent_write(event,buf,strlen(buf));
free(namelist);
return 0;
}
int SendFile(struct bufferevent *event,const char* filename)
{
int fd = open(filename,O_RDONLY);
if(fd >0)
{
#if 1
while(1)
{
char buf[1024];
int len = read(fd,buf,sizeof buf);
if(len >0)
{
//send(cfd,buf,len,0);
bufferevent_write(event,buf,len);
usleep(10);
}
else if(len == 0)
{
printf("Read file end\n");
break;
}
else
{
perror("read error");
}
}
#else
off_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 http_request(struct bufferevent *event,char *path)
{
char *file = NULL;
if(strcmp(path,"/") == 0)
{
file = "./";
}
else
{
file = path+1;
}
struct stat st;
int ret = stat(file,&st);
if(ret == -1)
{
printf("file doest not exist\n");
SendHead(event,404,"Not found",GetFileType(".html"),-1);
SendFile(event,"404.html");
return -1;
}
if(S_ISDIR(st.st_mode))
{
printf("Directory\n");
SendHead(event,200,"OK",GetFileType(".html"),-1);
SendDir(event,file);
}
else
{
printf("File\n");
SendHead(event,200,"OK",GetFileType(file),st.st_size);
SendFile(event,file);
}
return 0;
}
void read_cb(struct bufferevent *event,void *arg)
{
char buf[256] = {0};
char method[10]= {0},path[256]={0},protocol[10]={0};
int ret = bufferevent_read(event,buf,sizeof(buf));
if(ret >0)
{
sscanf(buf,"%[^ ] %[^ ] %[^ \r\n]",method,path,protocol);
if(strcasecmp(method,"get") == 0)
{
char bufline[256] = {0};
write(STDOUT_FILENO,buf,ret);
while((ret = bufferevent_read(event,bufline,sizeof(bufline)))>0)
{
write(STDOUT_FILENO,bufline,ret);
}
http_request(event,path);
}
}
}
void bevent_cb(struct bufferevent *event,short what,void *arg)
{
if(what & BEV_EVENT_EOF)
{
printf("client closeed\n");
bufferevent_free(event);
}
else if(what & BEV_EVENT_ERROR)
{
printf("client error\n");
bufferevent_free(event);
}
else if(what & BEV_EVENT_CONNECTED)
{
printf("new client connected\n");
}
}
void listener_cb(struct evconnlistener *listener,evutil_socket_t fd,struct sockaddr *addr,int socklen,void *arg)
{
struct event_base *base = (struct event_base*)arg;
struct bufferevent *event= bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(event,read_cb,NULL,bevent_cb,base);
bufferevent_enable(event,EV_READ|EV_WRITE);
}
int main (int argc ,char*argv[])
{
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
signal(SIGKILL, sig_handler);//Program can not recieve SIGKILL(9) signal so.... this cmd does not make any sense
// Ignore broken pipes
signal(SIGPIPE, SIG_IGN);
char work_dir[256]={0};
strcpy(work_dir,getenv("PWD"));
printf("dir:%s\n",work_dir);
chdir(work_dir);
struct event_base *base = event_base_new();
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(9999);
server.sin_addr.s_addr = htonl(INADDR_ANY);
struct evconnlistener *listener = evconnlistener_new_bind(base,listener_cb,base,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE_PORT,-1,
(struct sockaddr *)&server,sizeof(server));
event_base_dispatch(base);
event_base_free(base);
evconnlistener_free(listener);
printf("Exit normally\n");
return 0;
}