#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define _GNU_SOURCE
#include
#define
SERVER_NAME "tmhttpd/1.0.0_alpha"
#define
VERSION "1.0.0
alpha"
#define
BUFFER_SIZE 8192
#define
REQUEST_MAX_SIZE 10240
#define
IS_DEBUG 0 #define
IS_DAEMON 0 #define IS_BROWSE 0 #define
IS_LOG 0 #define
DEFAULT_HTTP_PORT 80 #define
DEFAULT_MAX_CLIENT 100 #define
DEFAULT_DOCUMENT_ROOT "./" #define
DEFAULT_DIRECTORY_INDEX "index.html" #define
DEFAULT_LOG_PATH "/tmp/tmhttpd.log"
struct st_request_info {
char *method;
char *pathinfo;
char *query;
char *protocal;
char *path;
char *file;
char *physical_path;
};
static unsigned short g_is_debug = IS_DEBUG;
static unsigned short g_is_daemon =
IS_DAEMON;
static unsigned short g_is_browse =
IS_BROWSE;
static unsigned short g_is_log =
IS_LOG;
static unsigned int
g_port =
DEFAULT_HTTP_PORT;
static unsigned int g_max_client =
DEFAULT_MAX_CLIENT;
static char
g_dir_index[32] =
DEFAULT_DIRECTORY_INDEX;
static char
g_doc_root[256] =
DEFAULT_DOCUMENT_ROOT;
static char
g_log_path[256] =
DEFAULT_LOG_PATH;
static int
g_log_fd =
0;
void die(char *mess){
perror(mess);
exit(1);
}
static char *substr( const char *s, int start_pos, int length, char
*ret ){
char
buf[length+1];
int i, j,
end_pos;
int str_len = strlen(s);
if
(str_len <= 0 || length < 0){
return
""; }
if (length == 0){
length = str_len -
start_pos;
}
start_pos = ( start_pos < 0 ?
(str_len + start_pos) : ( start_pos==0 ? start_pos : start_pos-- )
);
end_pos = start_pos + length;
for(i=start_pos, j=0; i<=length; i++, j++){
buf[j] =
s[i]; }
buf[length]
= '\0';
strcpy(ret,
buf);
return(ret);
}
static void explode(char *from, char delim, char ***to, int
*item_num){
int i, j, k,
n, temp_len;
int max_len
= strlen(from) + 1;
char
buf[max_len], **ret;
for(i=0,
n=1; from[i]!='\0'; i++){
if (from[i] == delim) n++;
}
ret = (char
**)malloc(n*sizeof(char *));
for (i=0,
k=0; k
memset(buf, 0,
max_len); for(j=0; from[i]!='\0' &&
from[i]!=delim; i++, j++) buf[j] = from[i];
i++;
temp_len = strlen(buf)+1;
ret[k] = malloc(temp_len);
memcpy(ret[k], buf, temp_len);
}
*to =
ret;
*item_num =
n;
}
static char *strtolower( char *s ){
int i, len = sizeof(s);
for( i = 0; i < len; i++ ){
s[i] = ( s[i] >=
'A' && s[i] <= 'Z' ?
s[i] + 'a' - 'A' : s[i] );
}
return(s);
}
static char *strtoupper( char *s ){
int i, len = sizeof(s);
for( i = 0; i < len; i++ ){
s[i] = ( s[i] >=
'a' && s[i] <= 'z' ?
s[i] + 'A' - 'a' : s[i] );
}
return(s);
}
static int strpos (const char *s, char c){
int i, len;
if (!s || !c) return -1;
len = strlen(s);
for (i=0; i
if (s[i] == c) return i;
}
return -1; }
static int strrpos (const char *s, char c){
int i, len;
if (!s || !c) return -1;
len = strlen(s);
for (i=len; i>=0; i--){
if (s[i] == c) return i;
}
return -1;
}
static char *trim( char *s
){ int
l; for(
l=strlen(s); l>0 &&
isspace((u_char)s[l-1]); l-- ){
s[l-1] =
'\0'; }
return(s); }
static char *ltrim( char *s
){ char
*p; for(p=s;
isspace((u_char)*p); p++
); if( p!=s )
strcpy(s, p); return(s); }
static long filesize(const char *filename){
struct stat
buf;
if
(!stat(filename, &buf)){
return buf.st_size;
}
return
0;
}
static int file_exists(const char *filename){
struct stat
buf;
if
(stat(filename, &buf) < 0){
if (errno == ENOENT){
return 0;
}
}
return
1;
}
static int file_get_contents( const char *filename, size_t
filesize, char *ret, off_t length ){
if ( !file_exists(filename) || access(filename,
R_OK)!=0 ) return -1;
int fd;
char buf[filesize];
if ( (fd = open(filename, O_RDONLY)) == -1)
return -1;
length = ( length > 0 ? length :
filesize);
read(fd, buf, length);
strcpy(ret, buf);
close(fd);
return 0;
}
static int is_dir(const char *filename){
struct stat buf;
if ( stat(filename, &buf)
< 0 ){
return -1;
}
if (S_ISDIR(buf.st_mode)){
return 1;
}
return 0;
}
static int is_file(const char *filename){
struct stat buf;
if ( stat(filename, &buf)
< 0 ){
return -1;
}
if (S_ISREG(buf.st_mode)){
return 1;
}
return 0;
}
static void getdate(char
*s){ //获得日期时间函数
char
*wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
time_t timep;
struct tm *p;
time(&timep);
p = localtime(&timep);
sprintf(s, "%d-%d-%d
%d:%d:%d",(1900+p->tm_year),
(1+p->tm_mon), p->tm_mday,
p->tm_hour, p->tm_min,
p->tm_sec);
}
static void mime_content_type( const char *name, char *ret
){ //根据文件名获取mime类型
char *dot,
*buf;
dot =
strrchr(name, '.'); //取得name字符串中'.'后的子字符串dot
if (
strcmp(dot, ".txt") == 0
){ //如果是.txt类型
buf = "text/plain";
} else if (
strcmp( dot, ".css" ) == 0 ){
buf = "text/css";
} else if (
strcmp( dot, ".js" ) == 0 ){
buf = "text/javascript";
} else if (
strcmp(dot, ".xml") == 0 || strcmp(dot, ".xsl") == 0 ){
buf = "text/xml";
} else if (
strcmp(dot, ".xhtm") == 0 || strcmp(dot, ".xhtml") == 0 ||
strcmp(dot, ".xht") == 0 ){
buf = "application/xhtml+xml";
} else if (
strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0 ||
strcmp(dot, ".shtml") == 0 || strcmp(dot, ".hts") == 0 ){
buf = "text/html";
} else if (
strcmp( dot, ".gif" ) == 0 ){
buf = "image/gif";
} else if (
strcmp( dot, ".png" ) == 0 ){
buf = "image/png";
} else if (
strcmp( dot, ".bmp" ) == 0 ){
buf = "application/x-MS-bmp";
} else if (
strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 ||
strcmp( dot, ".jpe" ) == 0 || strcmp( dot, ".jpz" ) == 0 ){
buf = "image/jpeg";
} else if (
strcmp( dot, ".wav" ) == 0 ){
buf = "audio/wav";
} else if (
strcmp( dot, ".wma" ) == 0 ){
buf = "audio/x-ms-wma";
} else if (
strcmp( dot, ".wmv" ) == 0 ){
buf = "audio/x-ms-wmv";
} else if (
strcmp( dot, ".au" ) == 0 || strcmp( dot, ".snd" ) == 0 ){
buf = "audio/basic";
} else if (
strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 ){
buf = "audio/midi";
} else if (
strcmp( dot, ".mp3" ) == 0 || strcmp( dot, ".mp2" ) == 0 ){
buf = "audio/x-mpeg";
} else if ( strcmp( dot, ".rm" ) ==
0 || strcmp( dot, ".rmvb" ) == 0 || strcmp( dot,
".rmm" ) == 0 ){
buf = "audio/x-pn-realaudio";
} else if (
strcmp( dot, ".avi" ) == 0 ){
buf = "video/x-msvideo";
} else if (
strcmp( dot, ".3gp" ) == 0 ){
buf = "video/3gpp";
} else if (
strcmp( dot, ".mov" ) == 0 ){
buf = "video/quicktime";
} else if (
strcmp( dot, ".wmx" ) == 0 ){
buf = "video/x-ms-wmx";
} else if ( strcmp( dot, ".asf" ) ==
0 || strcmp( dot, ".asx" ) == 0 ){
buf = "video/x-ms-asf";
} else if (
strcmp( dot, ".mp4" ) == 0 || strcmp( dot, ".mpg4" ) == 0 ){
buf = "video/mp4";
} else if ( strcmp( dot, ".mpe" ) ==
0 || strcmp( dot, ".mpeg" ) == 0 || strcmp( dot,
".mpg" ) == 0 || strcmp( dot, ".mpga" ) == 0 ){
buf = "video/mpeg";
} else if (
strcmp( dot, ".pdf" ) == 0 ){
buf = "application/pdf";
} else if (
strcmp( dot, ".rtf" ) == 0 ){
buf = "application/rtf";
} else if ( strcmp( dot, ".doc" ) ==
0 || strcmp( dot, ".dot" ) == 0 ){
buf = "application/msword";
} else if ( strcmp( dot, ".xls" ) ==
0 || strcmp( dot, ".xla" ) == 0 ){
buf = "application/msexcel";
} else if ( strcmp( dot, ".hlp" ) ==
0 || strcmp( dot, ".chm" ) == 0 ){
buf = "application/mshelp";
} else if ( strcmp( dot, ".swf" ) ==
0 || strcmp( dot, ".swfl" ) == 0 || strcmp( dot,
".cab" ) == 0 ){
buf = "application/x-shockwave-flash";
} else if ( strcmp( dot, ".ppt" ) ==
0 || strcmp( dot, ".ppz" ) == 0 || strcmp( dot,
".pps" ) == 0 || strcmp( dot, ".pot" ) == 0 ){
buf = "application/mspowerpoint";
} else if (
strcmp( dot, ".zip" ) == 0 ){
buf = "application/zip";
} else if (
strcmp( dot, ".rar" ) == 0 ){
buf = "application/x-rar-compressed";
} else if (
strcmp( dot, ".gz" ) == 0 ){
buf = "application/x-gzip";
} else if (
strcmp( dot, ".jar" ) == 0 ){
buf = "application/java-archive";
} else if ( strcmp( dot, ".tgz" ) ==
0 || strcmp( dot, ".tar" ) == 0 ){
buf = "application/x-tar";
} else {
buf =
"application/octet-stream";
}
strcpy(ret, buf);
}
static void Usage(char *exefile){
fprintf(stderr,
"#=======================================\n");
fprintf(stderr, "#
TieMa(Tiny&Mini) Http Server\n");
fprintf(stderr, "# Version %s\n", VERSION);
fprintf(stderr, "# \n");
fprintf(stderr, "# heiyeluren \n");
fprintf(stderr,
"#=======================================\n\n");
fprintf(stderr, "Usage: %s [OPTION] ... \n", exefile);
fprintf(stderr, "\nOptions: \n\
-D, --is-deubg Is open debug
mode, default No\n\
-d, --is-daemon Is daemon
running, default No\n\
-p, --port=PORT Server listen
port, default 80\n\
-m, --max-client=SIZE Max
connection requests, default 100\n\
-L,
--is-log Is write access log,
default No\n\
-l, --log-path=PATH Access log
path, default /tmp/tmhttpd.log\n\
-b, --is-browse Is allow browse
file/dir list, default No\n\
-r, --doc-root=PATH Web
document root directory, default programe current directory
./\n\
-i, --dir-index=FILE Directory
default index file name, default index.html\n\
-h,
--help Print help
information\n");
fprintf(stderr, "\nExample: \n\
%s -d -p 80 -m 128 -L -l /tmp/access.log -b -r
/var/www -i index.html\n\
%s -d -p80 -m128 -L -l/tmp/access.log -b
-r/var/www -iindex.html\n\
%s --is-daemon --port=80 --max-client=128
--is-log --log-path=/tmp/access.log --is-browse --doc-root=/var/www
--dir-index=index.html\n\n", exefile, exefile, exefile);
}
static void PrintConfig(){
fprintf(stderr,
"===================================\n");
fprintf(stderr, " tmhttpd Configure
information\n");
fprintf(stderr,
"===================================\n");
fprintf(stderr, "Is-Debug\t = %s\n", g_is_debug ?
"Yes" : "No");
fprintf(stderr, "Is-Daemon\t = %s\n", g_is_daemon
? "Yes" : "No");
fprintf(stderr, "Port\t\t = %d\n", g_port);
fprintf(stderr, "Max-Client\t = %d\n",
g_max_client);
fprintf(stderr, "Is-Log\t\t = %s\n", g_is_log ?
"Yes" : "No");
fprintf(stderr, "Log-Path\t = %s\n",
g_log_path);
fprintf(stderr, "Is-Browse\t = %s\n", g_is_browse
? "Yes" : "No");
fprintf(stderr, "Doc-Root\t = %s\n",
g_doc_root);
fprintf(stderr, "Dir-Index\t = %s\n",
g_dir_index);
fprintf(stderr,
"===================================\n\n");
}
static int WriteLog( const char *message ){
if ( !g_is_log ){
fprintf(stderr, "%s",
message);
return 0;
}
if ( g_log_fd == 0 ){
if ( (g_log_fd =
open(g_log_path, O_RDWR|O_CREAT|O_APPEND,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1 ){
perror("open
log file error");
return
-1;
}
}
if (write(g_log_fd, message, strlen(message)) ==
-1){
perror("write log
error");
return -1;
}
return 0;
}
static int SendHeaders(int client_sock, int status, char *title,
char *extra_header, char *mime_type, off_t length, time_t mod
){
time_t
now;
char
timebuf[100], buf[BUFFER_SIZE], buf_all[REQUEST_MAX_SIZE],
log[8];
memset(buf,
0, strlen(buf));
sprintf(buf,
"%s %d %s\r\n", "HTTP/1.0", status, title);
strcat(buf_all, buf);
memset(buf, 0, strlen(buf));
sprintf(buf,
"Server: %s\r\n", SERVER_NAME);
strcat(buf_all, buf);
now =
time( (time_t*) 0 );
strftime(
timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(
&now ) );
memset(buf,
0, strlen(buf));
sprintf(buf,
"Date: %s\r\n", timebuf);
strcat(buf_all, buf);
if
(extra_header != (char*)0){
memset(buf, 0, strlen(buf));
sprintf(buf, "%s\r\n", extra_header);
strcat(buf_all, buf);
}
if
(mime_type != (char*)0){
memset(buf, 0, strlen(buf));
sprintf(buf, "Content-Type: %s\r\n", mime_type);
strcat(buf_all, buf);
}
if (length
>= 0){
memset(buf, 0, strlen(buf));
sprintf(buf, "Content-Length: %lld\r\n",
(int64_t)length); strcat(buf_all, buf);
}
if (mod !=
(time_t) -1 ){
memset(buf, 0, strlen(buf));
strftime( timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT",
gmtime( &mod ) );
sprintf(buf, "Last-Modified: %s\r\n", timebuf );
strcat(buf_all, buf);
}
memset(buf,
0, strlen(buf));
sprintf(buf,
"Connection: close\r\n\r\n");
strcat(buf_all, buf);
if ( g_is_debug ){
fprintf(stderr, "[ Response
]\n");
fprintf(stderr, "%s",
buf_all);
}
write(client_sock, buf_all, strlen(buf_all));
return 0;
}
static int SendError(int client_sock, int status, char *title, char
*extra_header, char *text ){
char
buf[BUFFER_SIZE], buf_all[REQUEST_MAX_SIZE];
SendHeaders(
client_sock, status, title, extra_header, "text/html", -1, -1
);
memset(buf,
0, strlen(buf));
sprintf(buf,
"\n
\n\n\n\n
%d %s
\n", status, title, SERVER_NAME, status, title);
strcat(buf_all, buf);
memset(buf, 0, strlen(buf));
sprintf(buf,
"%s\n", text);
strcat(buf_all, buf);
memset(buf, 0, strlen(buf));
sprintf(buf,
"\n
\n
%s
\n\n\n", SERVER_NAME);
strcat(buf_all, buf);
write(client_sock, buf_all, strlen(buf_all));
exit(0);
}
static int SendFile( int client_sock, char *filename, char
*pathinfo ){ //发送文件函数
char
buf[128], contents[8192],
mime_type[64]; //定义字符数组buf, contents, mime_type
int
fd;
size_t
file_size;
//获得 mime 类型,发送http包头信息
mime_content_type(filename,
mime_type); //根据文件名filename获得mime 类型
SendHeaders(
client_sock, 200, "OK", "", mime_type, filesize(filename),
0); //发送http包头信息
if ( (fd =
open(filename, O_RDONLY)) == -1
){ //以只读的方式打开文件filename
memset(buf, '\0', sizeof(buf));
sprintf(buf, "Something unexpected went wrong read file %s.",
pathinfo);
SendError( client_sock, 500, "Internal Server Error", "",
buf);
}
file_size =
filesize(filename); //获得该文件的大小
if ( file_size <
8192){ //如果文件大小file_size小于8k
read(fd, contents,
8192); //将文件fd中的内容读入字符数组contents
write( client_sock, contents,
file_size ); //将该内容发送给客户端
} else
{ //如果文件大小file_size大于8k
while ( read(fd, contents,
8192) > 0
){ //进行循环读取发送,直到完成
write(
client_sock, contents, 8192 );
memset(contents,
'\0', sizeof(contents));
}
}
close( fd
);
if
(g_is_debug){
printf("request filename: %s\n", filename);
printf("request pathinfo: %s\n", pathinfo);
}
return 0;
}
static int SendDirectory( int client_sock, char *path, char
*pathinfo
){ //发送文件目录
size_t file_size;
char msg[128], buf[10240], tmp[1024],
tmp_path[1024]; //定义字符数组msg, buf,tmp,tmp_path
DIR
*dp; // 目录结构体指针
struct dirent
*node; //当前目录的节点
if ( (dp = opendir(path)) == NULL
){ //根据目录获得目录的结构体指针
memset(buf, 0,
sizeof(msg));
sprintf(msg, "Something
unexpected went wrong browse directory %s.", pathinfo);
SendError( client_sock, 500,
"Internal Server Error", "", msg);
}
memset(tmp_path, 0,
sizeof(tmp_path)); //初始化临时目录
substr(pathinfo, -1, 1,
tmp_path); //检测pathinfo中是否含有"/",如果没有,需要在tmp_path后追加'\'
if ( strcmp(tmp_path, "/") == 0){
memset(tmp_path, 0,
sizeof(tmp_path));
strcpy(tmp_path,
pathinfo);
} else {
memset(tmp_path, 0,
sizeof(tmp_path));
strcpy(tmp_path,
pathinfo); strcat(tmp_path, "/");
}
sprintf( tmp, "\n
\n\n\n
Directory %s
\n
\n
\n", pathinfo, SERVER_NAME,
pathinfo );
strcat(buf, tmp);
while ( (node = readdir(dp)) !=
NULL){ //从目录结构体指针dp中获得node结构体节点
if (
strcmp(node->d_name, ".") != 0){
memset(tmp,
0, sizeof(tmp));
sprintf(tmp,
"\t
\n", tmp_path,
node->d_name, node->d_name);
strcat(buf,
tmp);
}
}
memset(tmp,
0, sizeof(tmp));
sprintf(tmp,
"\n
\n
\n
%s
\n\n\n", SERVER_NAME);
strcat(buf,
tmp);
file_size = strlen(buf);
SendHeaders( client_sock, 200, "OK", "",
"text/html", file_size, 0 );
write( client_sock, buf, file_size );
return 0;
}
static int ProcRequest( int client_sock, struct sockaddr_in
client_addr, struct st_request_info request_info
){ //处理客户端的请求
char buf[128];
if ( !file_exists( request_info.physical_path )
){ //检测客户端请求的路径文件是否存在
memset(buf, 0,
sizeof(buf)); sprintf(buf, "File %s not
found.", request_info.pathinfo);
SendError( client_sock, 404,
"Not Found", "", buf);
}
if ( access(request_info.physical_path, R_OK) !=
0
){ //进入该路径
memset(buf, 0,
sizeof(buf));
sprintf(buf, "File %s is
protected.", request_info.pathinfo);
SendError( client_sock, 403,
"Forbidden", "", buf);
}
if ( is_file(request_info.physical_path) == 1
){ //判断是一个普通文件或者目录 SendFile(
client_sock, request_info.physical_path,
request_info.pathinfo
); //向客户端发送文件的路径和请求目录信息
} else if ( is_dir(request_info.physical_path)
== 1
){ //如果是一个目录
if ( g_is_browse ){
SendDirectory(
client_sock, request_info.physical_path, request_info.pathinfo );
//向客户端发送文件的路径和请求目录信息
} else {
memset(buf,
0, sizeof(buf));
sprintf(buf,
"File %s is protected.", request_info.pathinfo);
SendError(
client_sock, 403, "Forbidden", "", buf);
}
} else {
memset(buf, 0,
sizeof(buf));
sprintf(buf, "File %s is
protected.", request_info.pathinfo);
SendError( client_sock, 403,
"Forbidden", "", buf); }
return 0;
}
static int ParseRequest( int client_sock, struct sockaddr_in
client_addr, char *req ){ //这个就是客户端的请求消息
char **buf, **method_buf,
**query_buf, //定义字符串指针,指向常量字符串,分别为方法字符串,查询字符串
currtime[32], cwd[1024], tmp_path[1536],
pathinfo[512], //当前时间字符数组,临时目录,目录信息字符数组
path[256], file[256],
log[1024]; //目录字符串,文件字符数组,记录字符数组
int line_total, method_total, query_total,
i; //line_total所有的行,method_total所有的方法,query_total查询数
struct st_request_info
request_info; //定义一个请求信息结构体st_request_info 变量
getdate(currtime); //获得当前的信息
explode(req, '\n', &buf,
&line_total); //可能是获得请求信息的所有行数888888888888888888888888888888888888888888888888888、、、、、、、、、、、、、、、、、、、
memset(log, 0,
sizeof(log)); //初始化记录信息log,打印请求记录信息
sprintf(log, "[%s] %s %s\n", currtime,
inet_ntoa(client_addr.sin_addr),
buf[0]); WriteLog(log);
if (strcmp(buf[0], "\n") == 0 || strcmp(buf[0],
"\r\n") ==
0){ //通过对请求信息字符串的首字符串检测是否为'\n'和'\r\n'来判断是否为空
SendError( client_sock, 400,
"Bad Request", "", "Can't parse request."
); //如果为空就发送错误信息
}
explode(buf[0], ' ', &method_buf,
&method_total); //请求信息字符串数组的首字符串为 方法method_buf
if ( strcmp( strtolower(method_buf[0]), "get") !=
0 && strcmp(
strtolower(method_buf[0]), "head") != 0
){ //判断方法是不是get和head,如果不是发送错误报告
SendError( client_sock, 501,
"Not Implemented", "", "That method is not implemented." );
}
explode(method_buf[1], '?',
&query_buf,
&query_total); //请求字符串数组的第二个字符串为查询信息字符数组
getcwd(cwd,
sizeof(cwd)); strcpy(pathinfo,
query_buf[0]); //拷贝查询第一个字符串到(pathinfo
substr(query_buf[0], 0, strrpos(pathinfo, '/')+1,
path); //以'\'为分割点分割字符窜query_buf[0]
substr(query_buf[0], strrpos(pathinfo, '/')+1, 0,
file);
memset(&request_info, 0,
sizeof(request_info)); //初始化请求信息结构体
strcat(cwd, pathinfo);
request_info.method =
method_buf[0];
request_info.pathinfo =
pathinfo;
request_info.query =
(query_total == 2 ? query_buf[1] : "");
request_info.protocal =
strtolower(method_buf[2]);
request_info.path =
path;
request_info.file =
file;
request_info.physical_path =
cwd;
memset(tmp_path, 0,
sizeof(tmp_path)); //初始化临时目录
strcpy(tmp_path,
cwd); if ( is_dir(tmp_path)
){ strcat(tmp_path,
g_dir_index);
if ( file_exists(tmp_path)
){
request_info.physical_path
= tmp_path;
}
}
if ( g_is_debug ){
fprintf(stderr, "[ Request
]\n");
for(i=0; i
fprintf(stderr,
"%s\n", buf[i]);
}
}
ProcRequest( client_sock, client_addr,
request_info
); //处理该客户端的请求
return 0;
}
static void HandleClient( int client_sock, struct sockaddr_in
client_addr ){ //和客户端进行通信的函数
char
buf[REQUEST_MAX_SIZE]; //定义一个字符数组
if ( read(client_sock, buf, REQUEST_MAX_SIZE)
<
0){ //读取该客户端的消息
SendError( client_sock, 500,
"Internal Server Error", "", "Client request not success." );
die("read sock");
}
ParseRequest( client_sock, client_addr, buf
); //对读取到的请求消息进行处理
close(client_sock);
exit(0); //处理完之后安全退出进程
}
static void InitServerListen( unsigned int port, unsigned int
max_client ){ //初始化监听
int
serversock,
clientsock; //服务器套接字,客户端套接字
struct
sockaddr_in server_addr,
client_addr; //套接字地址
char
currtime[32]; //当前时间
if
((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))
<
0){ //创建服务器套接字
die("Failed to create socket");
}
//设置套接字地址
memset(&server_addr, 0,
sizeof(server_addr)); server_addr.sin_family =
AF_INET; server_addr.sin_addr.s_addr =
htonl(INADDR_ANY); server_addr.sin_port =
htons(port);
if
(bind(serversock, (struct sockaddr *) &server_addr,
sizeof(server_addr)) <
0){ //绑定套接字地址
die("Failed to bind the server socket");
}
if
(listen(serversock, max_client) <
0){ //开始进行监听
die("Failed to listen on server socket");
}
getdate(currtime); //获得当前时间
fprintf(stdout, "[%s] Start server listening at
port %d ...\n", currtime, port);
fprintf(stdout, "[%s] Waiting client connection
...\n", currtime);
while
(1){ //死循环进行阻塞监听
unsigned int clientlen = sizeof(client_addr);
memset(currtime, 0,
sizeof(currtime));
getdate(currtime);
if ((clientsock = accept(serversock, (struct sockaddr *)
&client_addr, &clientlen))
<
0){ //收到客户端的连接请求,为客户端分配相应的套接字
die("Failed to accept client connection");
}
if ( fork() == 0
){ //为客户端单独创建一个进程进行通信,此处应该是用到了多进程通信
HandleClient(clientsock,
client_addr); //进入客户端通信函数
} else {
wait(NULL); //创建进程失败 等待退出
}
close(clientsock);
}
}
static int ParseOptions( int argc, char *argv[] ){
int opt;
struct option longopts[] = {
{
"is-debug", 0, NULL, 'D' },
{
"is-daemon", 0, NULL, 'd' },
{
"port", 1, NULL, 'p' },
{
"max-client", 1, NULL, 'm' },
{
"is-log", 0, NULL, 'L' },
{
"log-path", 1, NULL, 'l' },
{
"is-browse", 0, NULL, 'b' },
{
"doc-root", 1, NULL, 'r' },
{
"dir-index", 1, NULL, 'i' },
{
"help", 0, NULL, 'h' },
{
0, 0,
0, 0 }
};
while ( (opt = getopt_long(argc, argv,
":Ddp:m:Ll:br:i:h", longopts, NULL)) != -1){
switch (opt){
case
'h':
Usage(argv[0]);
return(-1);
break;
case 'D':
g_is_debug = 1; break;
case 'd':
g_is_daemon = 1; break;
case
'p':
g_port
= atoi(optarg);
if
( g_port < 1 || g_port > 65535
){
fprintf(stderr,
"Options -p,--port error: input port number %s invalid, must
between 1 - 65535.\n\n", optarg);
return(-1);
}
break;
case
'm':
g_max_client
= atoi(optarg);
if
( !isdigit(g_max_client) ){
fprintf(stderr,
"Options -m,--max-client error: input clients %s invalid, must
number, proposal between 32 - 2048.\n\n", optarg);
return(-1);
}
break;
case 'L':
g_is_log = 1; break;
case
'l':
strcpy(g_log_path,
optarg);
if
( !file_exists(g_log_path) || !is_dir(g_log_path) ){
fprintf(stderr,
"Options -l,--log-path error: input path %s not exist or not a
directory.\n\n", optarg);
return(-1); }
break;
case 'b':
g_is_browse = 1; break;
case
'r':
strcpy(g_doc_root,
optarg);
if
( !file_exists(g_doc_root) || !is_dir(g_doc_root) ){
fprintf(stderr,
"Options -l,--log-path error: input path %s not exist or not a
directory.\n\n", optarg);
return(-1); }
break;
case
'i':
strcpy(g_dir_index,
optarg);
break;
}
}
return(0);
}
int main( int argc, char *argv[] ){
if ( argc > 1 ){
if ( ParseOptions( argc, argv )
!= 0 ){
exit(-1);
}
}
chdir(g_doc_root);
if ( g_is_daemon ){
pid_t pid;
if ( (pid = fork())
< 0 ){
die("daemon
fork error");
} else if ( pid != 0){
exit(1);
}
}
if ( g_is_debug ){
PrintConfig();
}
InitServerListen( g_port, g_max_client );
}