#ifndef _MY_UTILS_H_INCLUDE_
#define _MY_UTILS_H_INCLUDE_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <signal.h>
#define SERVER_PORT 8000
#define BUF_SIZE 1024
#define EPOLL_SIZE 1000
#define METHOD_GET 0
#define METHOD_HEAD 1
#define METHOD_UNSUPPORTED -1
#define SERVER_NAME "myserver/0.1"
#define DEFAULT_TYPE "text/plain"
#define oops(msg) {perror(msg);exit(0);}
typedef struct file_info_s file_info_t;
struct file_info_s {
char *file_name;
char *full_path;
file_info_t *next;
char *content;
size_t content_length;
char *content_type;
};
#endif
#include "my_utils.h"
static int handle_connection(int fd);
static void help();
static void sigcatch(int signal);
static int send_message(int fd,char*buffer,size_t len);
static int get_method(char*message);
static file_info_t *get_file(char*message);
file_info_t *files=NULL;
static char*msg404="Not found\r\n\r\n";
int main(int ac,char**av)
{
int client_fd,listen_fd,epoll_fd;
char *full_path=NULL,*dir_name=NULL;
DIR *dir=NULL;
struct dirent *cur_file=NULL;
struct stat cur_stat;
struct sockaddr_in server_addr,client_addr;
struct epoll_event ev,events[EPOLL_SIZE];
u_int i,events_count=0;
file_info_t *cur_file_info=NULL,*last_file_info=NULL;
socklen_t client_addr_len;
if(ac!=2){
help();
exit(0);
}
dir_name=av[ac-1];
if((dir=opendir(dir_name))==NULL){
printf("Invalid directory -%s.\n",dir_name);
oops("opendir");
}
while((cur_file=readdir(dir))!=NULL){
full_path=malloc(strlen(cur_file->d_name)+strlen(dir_name)+4);
sprintf(full_path,"%s%s",dir_name,cur_file->d_name);
lstat(full_path,&cur_stat);
if(S_ISREG(cur_stat.st_mode)&&(cur_file->d_name[0]!='.')){
if(files==NULL){
files=malloc(sizeof(file_info_t)+2);
cur_file_info=files;
}else{
cur_file_info=malloc(sizeof(file_info_t)+2);
}
cur_file_info->file_name=malloc(strlen(cur_file->d_name)+2);
strcpy(cur_file_info->file_name,cur_file->d_name);
cur_file_info->full_path=full_path;
cur_file_info->next=NULL;
cur_file_info->content=NULL;
if(last_file_info==NULL)
last_file_info=cur_file_info;
else{
last_file_info->next=cur_file_info;
last_file_info=cur_file_info;
}
}else{
free(full_path);
}
}
(void)signal(SIGINT,sigcatch);
(void)signal(SIGTERM,sigcatch);
listen_fd=socket(AF_INET,SOCK_STREAM,0);
if(listen_fd<0)
oops("Socket");
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SERVER_PORT);
server_addr.sin_addr.s_addr=INADDR_ANY;
if(bind(listen_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
oops("bind");
if(listen(listen_fd,5)<0)
oops("listen");
epoll_fd=epoll_create(EPOLL_SIZE);
if(epoll_fd<0)
oops("epoll_create");
ev.events=EPOLLIN;
ev.data.fd=listen_fd;
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_fd,&ev)<0)
oops("epoll_ctl");
for(;;){
events_count=epoll_wait(epoll_fd,events,EPOLL_SIZE,-1);
if(events_count<0)
oops("epoll_wait");
for(i=0;i<events_count;i++){
if(events[i].data.fd==listen_fd){
client_addr_len=sizeof(struct sockaddr_in);
client_fd=accept(listen_fd,(struct sockaddr*)&client_addr,&client_addr_len);
if(client_fd<0)
oops("accept");
ev.data.fd=client_fd;
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,client_fd,&ev)<0)
oops("epoll_ctl");
}else{
if(handle_connection(events[i].data.fd)<0)
oops("handle_connection");
}
}
}
close(listen_fd);
return 0;
}
static int
handle_connection(int fd)
{
int rc,i,file_fd,method;
char message[BUF_SIZE],outb[BUF_SIZE];
char *out;
struct stat cur_stat;
file_info_t *file;
rc=recv(fd,message,BUF_SIZE,0);
if(rc<0){
printf("recv wrong");
close(fd);
return -1;
}
for(i=0;i<rc;i++){
if(message[i]=='\n'||message[i]=='\r')
break;
}
if(i>rc-2){
printf("Invalid request 1\n");
close(fd);
return -1;
}
message[i]='\0';//cut
method=get_method(message);
if(method==METHOD_UNSUPPORTED){
printf("Invalid request 2\n");
close(fd);
return -1;
}
file=get_file(message);
if(file==NULL){
out="HTTP/1.1 404 Not Found\r\n";
if(send_message(fd,out,strlen(out))<0)
return -1;
out="Content-Type:text/html\r\n";
if(send_message(fd,out,strlen(out))<0)
return -1;
sprintf(outb,"Server:%s\r\nContent-Length:%d\r\n\r\n",
SERVER_NAME,strlen(msg404));
if(send_message(fd,outb,strlen(outb))<0)
return -1;
close(fd);
return 1;
}
out="HTTP/1.1 200 OK\r\n";
if(send_message(fd,out,strlen(out))<0)
return -1;
if(file->file_name!=NULL){
lstat(file->full_path,&cur_stat);
sprintf(outb,"Content-Lenght:%d\r\nServer:%s\r\n\r\n",
cur_stat.st_size,SERVER_NAME);
if(send_message(fd,outb,strlen(outb))<0)
return -1;
if(method==METHOD_GET){
file_fd=open(file->full_path,O_RDONLY);
if(file_fd==-1){
printf("Couldn't open %s\n",file->full_path);
close(fd);
return -1;
}
sendfile(fd,file_fd,0,cur_stat.st_size);
close(file_fd);
}else if(method==METHOD_HEAD)
return 1;
}
close(fd);
}
static void
sigcatch(int signal)
{
printf("Signal Caugth ,exiting...\n");
exit(0);
}
static void help()
{
printf("usage:./main dirname\n");
}
static int send_message(int fd,char*buffer,size_t len)
{
int rc;
rc=send(fd,buffer,len,0);
if(rc<0){
printf("send error\n");
close(fd);
return -1;
}
return rc;
}
static int
get_method(char*req)
{
if(strncasecmp(req,"GET",3)==0)
return METHOD_GET;
if(strncasecmp(req,"HEAD",4)==0)
return METHOD_HEAD;
return METHOD_UNSUPPORTED;
}
static file_info_t*
get_file(char*req)
{
file_info_t *file_found=NULL;
char *uri=NULL;
u_int i;
if(files==NULL)
return NULL;
for(i=0;i<strlen(req);i++){
if(req[i]==' '){
if(uri==NULL)
uri=req+i+1;
else
break;
}
}
if(i>=strlen(req)){
return NULL;
}
req[i]='\0'; //cut again;
if(strcmp(uri,"/")==0){
uri="/index.html";
}
if(uri[0]=='/')
uri++;
file_found=files;
while(1){
if(strcasecmp(uri,file_found->file_name)==0)
return file_found;
if(file_found->next==NULL)
return NULL;
file_found=file_found->next;
}
return NULL;
}
obj=my_server.o
main=main
$(main):$(obj)
gcc -g -o $(main) $(obj)
my_server.o:
gcc -g -c my_server.c
clean:
rm -rf $(main) $(obj)
开启server :./main dir_name
访问:curl http://locahost:8000/test.txt (test.txt是你指定的目录dir_name下面的一个常规文件)