/*****************************************************************
system:ubunt12.04
enviroment:gcc
date:20180724
author:xiancan.wang
*****************************************************************/
//for exit()
#include <stdlib.h>
//for stdin , perro()
#include <stdio.h>
#include <errno.h>
//socket
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <string.h>
#define MAXDATASIZE 1024
#define SRC_PATH "./data"
int fileordirExist(char* fpath)
{
struct stat filestat;
printf("fpath:%s\n",fpath);
return ( stat(fpath,&filestat) != -1);
}
// is the filepath a directory
int IsDIR(char* fpath)
{
struct stat filestat;
return ( stat(fpath,&filestat) != -1 && S_ISDIR(filestat.st_mode));
}
const char* getextname(const char* filepath)
{
const char* p = NULL;
if(( p = strrchr(filepath,'.')) != NULL)
return p+1;
return NULL;
}
//write the packet header to the client
int writehead(FILE* cfp, const char* extname)
{
char* content = "text/plain";
if( strcmp(extname,"html") == 0 || strcmp(extname,"htm") == 0)
content = "text/html";
else if ( strcmp(extname,"css") == 0 )
content = "text/css";
else if ( strcmp(extname,"gif") == 0 )
content = "image/gif";
else if ( strcmp(extname,"jpeg") == 0 || strcmp(extname,"jpg") == 0)
content = "image/jpeg";
else if ( strcmp(extname,"png") == 0)
content = "image/png";
//必须以\r\n结尾
fprintf(cfp,"HTTP/1.1 200 OK \r\n");
fprintf(cfp,"Content-Type: %s \r\n",content);
//printf("HTTP/1.1 200 OK \r\n");
//printf("Content-Type: %s \r\n",content);
return 0;
}
//send the data of the file which the client want
void sendobj(int connectfd,const char* serverfilepath)
{
FILE* sfp =NULL,*cfp=NULL;
int c;
sfp = fopen(serverfilepath,"r");
if(sfp == NULL)
{
printf("erro to openfile %s\n",serverfilepath);
return;
}
cfp = fdopen(connectfd,"w");
writehead(cfp,getextname(serverfilepath));
//发送完head,开始发送body前必须先空一行
fprintf(cfp,"\r\n");
while( (c = getc(sfp)) != EOF)putc(c,cfp);
fflush(cfp);
return ;
}
//send the 404 error message to the client
void msg404(int connectfd)
{
const char* msg;
msg = "HTTP/1.0 404 Not Found \r\n Content-Type: text/plain 404 not found by Manio\r\n";
send(connectfd,msg,strlen(msg),0);
printf("404\n");
}
int do_requst(int connect_fd)
{
FILE *net_fd = 0;
char requestline[MAXDATASIZE]={0};
char cmd[128]={0};
char filepath[512]={0};
//当前目录作为根目录
snprintf(filepath,sizeof(filepath),SRC_PATH);
//重新以只读方式打开
net_fd = fdopen(connect_fd,"r");
fgets(requestline,MAXDATASIZE,net_fd);
printf("Recv:\n***************\n%s***************\n",requestline);
//strlen,不计算'\0'
sscanf(requestline,"%s %s ",cmd,filepath+strlen(SRC_PATH));
//printf("cmd:%s filepath:%s\n",cmd,filepath);
if(!strcmp("GET",cmd))
{//处理get请求
//
if(fileordirExist(filepath))
{
if(IsDIR(filepath))
{//请求目录,直接给当前目录的html
sendobj(connect_fd,"./data/index.html");
}
else
{
sendobj(connect_fd,filepath);
}
}
else
{
printf("erro %d:%s\n",errno,strerror(errno));
msg404(connect_fd);
}
}
else
{
//暂未支持
}
}
//./server ip port
int main(int argc, const char *argv[])
{
int i;
int n;
int ret;
int maxfd ;
char buf[1024];
pthread_t tid;
int listen_fd;
int connect_fd;
fd_set fd_table;
fd_set bak_table;
struct sockaddr_in peer_addr;
struct sockaddr_in server_addr;
socklen_t addrlen = sizeof(struct sockaddr);
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port\n",argv[0]);
exit(EXIT_FAILURE);
}
//1.0
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0){
perror("Fail to socket");
exit(EXIT_FAILURE);
}
//2.0
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
{
perror("Fail to bind");
exit(EXIT_FAILURE);
}
//3.0 设为监听模式
listen(listen_fd,128);
printf("Listen ....\n");
//初始化fd
FD_ZERO(&bak_table);
FD_SET(listen_fd,&bak_table);
maxfd = listen_fd;//
while(1)
{
fd_table = bak_table;
if(select(maxfd + 1,&fd_table,NULL,NULL,NULL) < 0)
{
perror("Fail to select");
exit(EXIT_FAILURE);
}
//0 1 2 分别为标准输入,输出,出错。所有从3开始
for(i = 3;i <= maxfd;i ++)
{
if(FD_ISSET(i,&fd_table))
{
if(i == listen_fd) //有链接返回
{
connect_fd = accept(listen_fd,(struct sockaddr *)&peer_addr,&addrlen);
if(connect_fd < 0)
{
perror("Fail to accept");
exit(EXIT_FAILURE);
}
printf("--------------------------------------\n");
printf("A new connection builed!\n");
printf("Ip : %s\n",inet_ntoa(peer_addr.sin_addr));
printf("Port : %d\n",ntohs(peer_addr.sin_port));
printf("--------------------------------------\n");
//将建立的新链接放到监控字符集合
FD_SET(connect_fd,&bak_table);
if(maxfd < connect_fd)
{
maxfd = connect_fd;
}
}
else
{//有请求
do_requst(i);
//做完一次请求必须关闭,http是短连接
//如不关闭,浏览器可能一直在等待
FD_CLR(i,&bak_table);
close(i);
}
}
}
}
close(listen_fd);
return 0;
}