一个web服务器的实现
时间:2007-12-27
来源:互联网
一个web服务器的实现
客户端(浏览器)与web服务器之间的交互主要包含客户的请求和服务器的应答。
请求和应答的格式在超文本传输协议(HTTP)中有定义。HTTP协议使用纯文本。
例如:
$telnet www.ouc.edu.cn 80
Trying 211.64.150.68...
Connected to www.ouc.edu.cn(211.64.150.68)
Escape character is '^]'.
GET /index.html HTTP/1.0
HTTP/1.1 200 OK
Date: Wed, 21 Jun 2006 08:26:04 GMT
Server: Apache/2.0.54(Unix) DAV/2
..................................
原理是telnet创建了一个socket并调用了connect来连接到web服务器。服务器
接受连接请求,并创建了一个基于socket的从客户端的键盘到web服务进程的数据通道。接下来,客户端输入请求:
GET /index.html HTTP/1.0
一个HTTP请求包含有3个字符串。第一个字符串是命令,第二个是参数,第三个是所用协议的版本号。 *注意还有一空行
web服务器读取请求,检查请求,然后返回一个请求。应答有两部分:头部和内容。头部以状态行起始,如下所示:
HTTP/1.1 200 ok
状态行含有两个或更多的字符串。第一个串是协议的版本,第二个串是返回码,这里是200,其文本解释是OK。头部的其余部分是关于应答的附加信息。
应答的其余部分是返回的具体内容。
下面这个webserv.c就是一个web服务器的具体实现。
编译程序,在某个端口运行它:
$gcc webserv.c -o webserv
$./webserv 12345
就可以访问web服务器,网址为http://yourhostname:12345/
将html文件放到该目录中并且用http://yourhostname:12345//filename.html就可以访问。
/* webserv.c - a web server
* build: gcc webserv.c -o webserv
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
#define HOSTLEN 256
#define BACKLOG 1
main(int ac, char *av[])
{
int sock, fd;
FILE *fpin;
char request[BUFSIZ];
if ( ac == 1 ){
fprintf(stderr,"usage: ws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) exit(2);
/* main loop here */
while(1){
/* take a call and buffer it */
fd = accept( sock, NULL, NULL );
fpin = fdopen(fd, "r" );
/* read request */
fgets(request,BUFSIZ,fpin);
printf("got a call: request = %s", request);
read_til_crnl(fpin);
/* do what client asks */
process_rq(request, fd);
fclose(fpin);
}
}
/* ------------------------------------------------------ *
make_server_socket(int portnum)
创建一个服务器套接字,并调用listen监听
------------------------------------------------------ */
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr; /* build our address here */
struct hostent *hp; /* this is part of our */
char hostname[HOSTLEN]; /* address */
int sock_id; /* the socket */
sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */
if ( sock_id == -1 )
return -1;
/** build address and bind it to socket **/
bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */
gethostname(hostname, HOSTLEN); /* where am I ? */
hp = gethostbyname(hostname); /* get info about host */
/* fill in host part */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum); /* fill in socket port */
saddr.sin_family = AF_INET ; /* fill in addr family */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
return -1;
/** arrange for incoming calls **/
if ( listen(sock_id, backlog) != 0 )
return -1;
return sock_id;
}
/* ------------------------------------------------------ *
read_til_crnl(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */
read_til_crnl(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
;
}
/* ------------------------------------------------------ *
process_rq( char *rq, int fd )
分析客户端的请求,分析然后做出相应的应答
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */
process_rq( char *rq, int fd )
{
char cmd[BUFSIZ], arg[BUFSIZ];
/* create a new process and return if not the child */
if ( fork() != 0 )
return;
strcpy(arg, "./"); /* precede args with ./ */
if ( sscanf(rq, "%s%s", cmd, arg+2) != 2 )
return;
if ( strcmp(cmd,"GET") != 0 )
cannot_do(fd);
else if ( not_exist( arg ) )
do_404(arg, fd );
else if ( isadir( arg ) )
do_ls( arg, fd );
else if ( ends_in_cgi( arg ) )
do_exec( arg, fd );
else
do_cat( arg, fd );
}
/* ------------------------------------------------------ *
the reply header thing: all functions need one
if content_type is NULL then don't send content type
------------------------------------------------------ */
header( FILE *fp, char *content_type )
{
fprintf(fp, "HTTP/1.0 200 OK\r\n");
if ( content_type )
fprintf(fp, "Content-type: %s\r\n", content_type );
}
/* ------------------------------------------------------ *
simple functions first:
cannot_do(fd) unimplemented HTTP command
and do_404(item,fd) no such object
------------------------------------------------------ */
cannot_do(int fd)
{
FILE *fp = fdopen(fd,"w");
fprintf(fp, "HTTP/1.0 501 Not Implemented\r\n");
fprintf(fp, "Content-type: text/plain\r\n");
fprintf(fp, "\r\n");
fprintf(fp, "That command is not yet implemented\r\n");
fclose(fp);
}
do_404(char *item, int fd)
{
FILE *fp = fdopen(fd,"w");
fprintf(fp, "HTTP/1.0 404 Not Found\r\n");
fprintf(fp, "Content-type: text/plain\r\n");
fprintf(fp, "\r\n");
fprintf(fp, "The item you requested: %s\r\nis not found\r\n",
item);
fclose(fp);
}
/* ------------------------------------------------------ *
the directory listing section
isadir() uses stat, not_exist() uses stat
do_ls runs ls. It should not
------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}
not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}
do_ls(char *dir, int fd)
{
FILE *fp ;
fp = fdopen(fd,"w");
header(fp, "text/plain");
fprintf(fp,"\r\n");
fflush(fp);
dup2(fd,1);
dup2(fd,2);
close(fd);
execlp("ls","ls","-l",dir,NULL);
perror(dir);
exit(1);
}
/* ------------------------------------------------------ *
the cgi stuff. function to check extension and
one to run the program.
------------------------------------------------------ */
char * file_type(char *f)
/* returns 'extension' of file */
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}
ends_in_cgi(char *f)
{
return ( strcmp( file_type(f), "cgi" ) == 0 );
}
do_exec( char *prog, int fd )
{
FILE *fp ;
fp = fdopen(fd,"w");
header(fp, NULL);
fflush(fp);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
execl(prog,prog,NULL);
perror(prog);
}
/* ------------------------------------------------------ *
do_cat(filename,fd)
sends back contents after a header
------------------------------------------------------ */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *content = "text/plain";
FILE *fpsock, *fpfile;
int c;
if ( strcmp(extension,"html") == 0 )
content = "text/html";
else if ( strcmp(extension, "gif") == 0 )
content = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
content = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
content = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
header( fpsock, content );
fprintf(fpsock, "\r\n");
while( (c = getc(fpfile) ) != EOF )
putc(c, fpsock);
fclose(fpfile);
fclose(fpsock);
}
exit(0);
}
[ 本帖最后由 liuke432 于 2006-6-21 16:46 编辑 ]
er 回复于:2006-06-21 15:58:19
支持
kuaizaifeng 回复于:2006-06-21 16:12:56
好东东,支持
cmh_2003 回复于:2006-06-21 17:43:24
先顶下慢慢看~~~
HAMMER 回复于:2006-06-21 17:52:32
新到任的高手啊
linuxcici 回复于:2006-06-21 18:38:16
请问WEB服务器写的时候要注意点什么呢? 小弟正在学. ....好象很多书有关于这web server的code.
liuke432 回复于:2006-06-22 09:27:42
引用:原帖由 HAMMER 于 2006-6-21 17:52 发表
新到任的高手啊
多谢兄弟抬举,我还称不上是什么高手。 :)
学习unix网络编程才半年多,看到有本书上的这个例子比较不错,
对于学习网络编程的朋友很有实践价值。于是就整理了一下,让大
家一起学习。
liuke432 回复于:2006-06-22 09:41:22
引用:原帖由 linuxcici 于 2006-6-21 18:38 发表
请问WEB服务器写的时候要注意点什么呢? 小弟正在学. ....好象很多书有关于这web server的code.
我对这个还不是很精通。 :(
web服务器的侧重点不一样,有的提供了更多的安全特征,有的则快速处理请求或使用较少的内存。
其中一个重要的特征是服务器的效率问题。服务器可以同时处理多少个请求?对于每个请求,服务器需要多少系统资源?
上面那个程序是用多进程来处理的。
下面再贴一个多线程的。
liuke432 回复于:2006-06-22 09:55:46
多线程的web服务器
多线程版本增加了内部统计功能:
服务器的运行时间
接收的客户端请求的数目
发送回客户端的数据量
这里使用独立线程(Detached Threads)防止僵尸线程(Zombie Threads)
/* webserv_pthread.c - a threaded web server
* building: gcc webserv_pthread.c -lpthread -o webserv_pthread
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#define HOSTLEN 256
#define BACKLOG 1
/* server facts here */
time_t server_started ;
int server_bytes_sent;
int server_requests;
main(int ac, char *av[])
{
int sock, fd;
int *fdptr;
pthread_t worker;
pthread_attr_t attr;
void *handle_call(void *);
if ( ac == 1 ){
fprintf(stderr,"usage: tws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) { perror("making socket"); exit(2); }
setup(&attr);
/* main loop here: take call, handle call in new thread */
while(1){
fd = accept( sock, NULL, NULL );
server_requests++;
fdptr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker,&attr,handle_call,fdptr);
}
}
/* ------------------------------------------------------ *
make_server_socket(int portnum)
创建一个服务器套接字,并调用listen监听
------------------------------------------------------ */
int make_server_socket_q(int , int );
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr; /* build our address here */
struct hostent *hp; /* this is part of our */
char hostname[HOSTLEN]; /* address */
int sock_id; /* the socket */
sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */
if ( sock_id == -1 )
return -1;
/** build address and bind it to socket **/
bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */
gethostname(hostname, HOSTLEN); /* where am I ? */
hp = gethostbyname(hostname); /* get info about host */
/* fill in host part */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum); /* fill in socket port */
saddr.sin_family = AF_INET ; /* fill in addr family */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
return -1;
/** arrange for incoming calls **/
if ( listen(sock_id, backlog) != 0 )
return -1;
return sock_id;
}
/*
* initialize the status variables and
* set the thread attribute to detached
*/
setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);
time(&server_started);
server_requests = 0;
server_bytes_sent = 0;
}
void *handle_call(void *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd ;
fd = *(int *)fdptr;
free(fdptr); /* get fd from arg */
fpin = fdopen(fd, "r"); /* buffer input */
fgets(request,BUFSIZ,fpin); /* read client request */
printf("got a call on %d: request = %s", fd, request);
skip_rest_of_header(fpin);
process_rq(request, fd); /* process client rq */
fclose(fpin);
}
/* ------------------------------------------------------ *
skip_rest_of_header(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */
skip_rest_of_header(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
;
}
/* ------------------------------------------------------ *
process_rq( char *rq, int fd )
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */
process_rq( char *rq, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
if ( sscanf(rq, "%s%s", cmd, arg) != 2 )
return;
sanitize(arg);
printf("sanitized version is %s\n", arg);
if ( strcmp(cmd,"GET") != 0 )
not_implemented();
else if ( built_in(arg, fd) )
;
else if ( not_exist( arg ) )
do_404(arg, fd);
else if ( isadir( arg ) )
do_ls( arg, fd );
else
do_cat( arg, fd );
}
/*
* make sure all paths are below the current directory
*/
sanitize(char *str)
{
char *src, *dest;
src = dest = str;
while( *src ){
if( strncmp(src,"/../",4) == 0 )
src += 3;
else if ( strncmp(src,"//",2) == 0 )
src++;
else
*dest++ = *src++;
}
*dest = '\0';
if ( *str == '/' )
strcpy(str,str+1);
if ( str[0]=='\0' || strcmp(str,"./")==0 || strcmp(str,"./..")==0 )
strcpy(str,".");
}
/* handle built-in URLs here. Only one so far is "status" */
built_in(char *arg, int fd)
{
FILE *fp;
if ( strcmp(arg,"status") != 0 )
return 0;
http_reply(fd, &fp, 200, "OK", "text/plain",NULL);
fprintf(fp,"Server started: %s", ctime(&server_started));
fprintf(fp,"Total requests: %d\n", server_requests);
fprintf(fp,"Bytes sent out: %d\n", server_bytes_sent);
fclose(fp);
return 1;
}
http_reply(int fd, FILE **fpp, int code, char *msg, char *type, char *content)
{
FILE *fp = fdopen(fd, "w");
int bytes = 0;
if ( fp != NULL ){
bytes = fprintf(fp,"HTTP/1.0 %d %s\r\n", code, msg);
bytes += fprintf(fp,"Content-type: %s\r\n\r\n", type);
if ( content )
bytes += fprintf(fp,"%s\r\n", content);
}
fflush(fp);
if ( fpp )
*fpp = fp;
else
fclose(fp);
return bytes;
}
/* ------------------------------------------------------ *
simple functions first:
not_implemented(fd) unimplemented HTTP command
and do_404(item,fd) no such object
------------------------------------------------------ */
not_implemented(int fd)
{
http_reply(fd,NULL,501,"Not Implemented","text/plain",
"That command is not implemented");
}
do_404(char *item, int fd)
{
http_reply(fd,NULL,404,"Not Found","text/plain",
"The item you seek is not here");
}
/* ------------------------------------------------------ *
the directory listing section
isadir() uses stat, not_exist() uses stat
------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}
not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}
do_ls(char *dir, int fd)
{
DIR *dirptr;
struct dirent *direntp;
FILE *fp;
int bytes = 0;
bytes = http_reply(fd,&fp,200,"OK","text/plain",NULL);
bytes += fprintf(fp,"Listing of Directory %s\n", dir);
if ( (dirptr = opendir(dir)) != NULL ){
while( direntp = readdir(dirptr) ){
bytes += fprintf(fp, "%s\n", direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}
/* ------------------------------------------------------ *
functions to cat files here.
file_type(filename) returns the 'extension': cat uses it
------------------------------------------------------ */
char * file_type(char *f)
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}
/* do_cat(filename,fd): sends header then the contents */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *type = "text/plain";
FILE *fpsock, *fpfile;
int c;
int bytes = 0;
if ( strcmp(extension,"html") == 0 )
type = "text/html";
else if ( strcmp(extension, "gif") == 0 )
type = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
type = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
type = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
bytes = http_reply(fd,&fpsock,200,"OK",type,NULL);
while( (c = getc(fpfile) ) != EOF ){
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}
客户端(浏览器)与web服务器之间的交互主要包含客户的请求和服务器的应答。
请求和应答的格式在超文本传输协议(HTTP)中有定义。HTTP协议使用纯文本。
例如:
$telnet www.ouc.edu.cn 80
Trying 211.64.150.68...
Connected to www.ouc.edu.cn(211.64.150.68)
Escape character is '^]'.
GET /index.html HTTP/1.0
HTTP/1.1 200 OK
Date: Wed, 21 Jun 2006 08:26:04 GMT
Server: Apache/2.0.54(Unix) DAV/2
..................................
原理是telnet创建了一个socket并调用了connect来连接到web服务器。服务器
接受连接请求,并创建了一个基于socket的从客户端的键盘到web服务进程的数据通道。接下来,客户端输入请求:
GET /index.html HTTP/1.0
一个HTTP请求包含有3个字符串。第一个字符串是命令,第二个是参数,第三个是所用协议的版本号。 *注意还有一空行
web服务器读取请求,检查请求,然后返回一个请求。应答有两部分:头部和内容。头部以状态行起始,如下所示:
HTTP/1.1 200 ok
状态行含有两个或更多的字符串。第一个串是协议的版本,第二个串是返回码,这里是200,其文本解释是OK。头部的其余部分是关于应答的附加信息。
应答的其余部分是返回的具体内容。
下面这个webserv.c就是一个web服务器的具体实现。
编译程序,在某个端口运行它:
$gcc webserv.c -o webserv
$./webserv 12345
就可以访问web服务器,网址为http://yourhostname:12345/
将html文件放到该目录中并且用http://yourhostname:12345//filename.html就可以访问。
/* webserv.c - a web server
* build: gcc webserv.c -o webserv
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
#define HOSTLEN 256
#define BACKLOG 1
main(int ac, char *av[])
{
int sock, fd;
FILE *fpin;
char request[BUFSIZ];
if ( ac == 1 ){
fprintf(stderr,"usage: ws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) exit(2);
/* main loop here */
while(1){
/* take a call and buffer it */
fd = accept( sock, NULL, NULL );
fpin = fdopen(fd, "r" );
/* read request */
fgets(request,BUFSIZ,fpin);
printf("got a call: request = %s", request);
read_til_crnl(fpin);
/* do what client asks */
process_rq(request, fd);
fclose(fpin);
}
}
/* ------------------------------------------------------ *
make_server_socket(int portnum)
创建一个服务器套接字,并调用listen监听
------------------------------------------------------ */
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr; /* build our address here */
struct hostent *hp; /* this is part of our */
char hostname[HOSTLEN]; /* address */
int sock_id; /* the socket */
sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */
if ( sock_id == -1 )
return -1;
/** build address and bind it to socket **/
bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */
gethostname(hostname, HOSTLEN); /* where am I ? */
hp = gethostbyname(hostname); /* get info about host */
/* fill in host part */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum); /* fill in socket port */
saddr.sin_family = AF_INET ; /* fill in addr family */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
return -1;
/** arrange for incoming calls **/
if ( listen(sock_id, backlog) != 0 )
return -1;
return sock_id;
}
/* ------------------------------------------------------ *
read_til_crnl(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */
read_til_crnl(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
;
}
/* ------------------------------------------------------ *
process_rq( char *rq, int fd )
分析客户端的请求,分析然后做出相应的应答
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */
process_rq( char *rq, int fd )
{
char cmd[BUFSIZ], arg[BUFSIZ];
/* create a new process and return if not the child */
if ( fork() != 0 )
return;
strcpy(arg, "./"); /* precede args with ./ */
if ( sscanf(rq, "%s%s", cmd, arg+2) != 2 )
return;
if ( strcmp(cmd,"GET") != 0 )
cannot_do(fd);
else if ( not_exist( arg ) )
do_404(arg, fd );
else if ( isadir( arg ) )
do_ls( arg, fd );
else if ( ends_in_cgi( arg ) )
do_exec( arg, fd );
else
do_cat( arg, fd );
}
/* ------------------------------------------------------ *
the reply header thing: all functions need one
if content_type is NULL then don't send content type
------------------------------------------------------ */
header( FILE *fp, char *content_type )
{
fprintf(fp, "HTTP/1.0 200 OK\r\n");
if ( content_type )
fprintf(fp, "Content-type: %s\r\n", content_type );
}
/* ------------------------------------------------------ *
simple functions first:
cannot_do(fd) unimplemented HTTP command
and do_404(item,fd) no such object
------------------------------------------------------ */
cannot_do(int fd)
{
FILE *fp = fdopen(fd,"w");
fprintf(fp, "HTTP/1.0 501 Not Implemented\r\n");
fprintf(fp, "Content-type: text/plain\r\n");
fprintf(fp, "\r\n");
fprintf(fp, "That command is not yet implemented\r\n");
fclose(fp);
}
do_404(char *item, int fd)
{
FILE *fp = fdopen(fd,"w");
fprintf(fp, "HTTP/1.0 404 Not Found\r\n");
fprintf(fp, "Content-type: text/plain\r\n");
fprintf(fp, "\r\n");
fprintf(fp, "The item you requested: %s\r\nis not found\r\n",
item);
fclose(fp);
}
/* ------------------------------------------------------ *
the directory listing section
isadir() uses stat, not_exist() uses stat
do_ls runs ls. It should not
------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}
not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}
do_ls(char *dir, int fd)
{
FILE *fp ;
fp = fdopen(fd,"w");
header(fp, "text/plain");
fprintf(fp,"\r\n");
fflush(fp);
dup2(fd,1);
dup2(fd,2);
close(fd);
execlp("ls","ls","-l",dir,NULL);
perror(dir);
exit(1);
}
/* ------------------------------------------------------ *
the cgi stuff. function to check extension and
one to run the program.
------------------------------------------------------ */
char * file_type(char *f)
/* returns 'extension' of file */
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}
ends_in_cgi(char *f)
{
return ( strcmp( file_type(f), "cgi" ) == 0 );
}
do_exec( char *prog, int fd )
{
FILE *fp ;
fp = fdopen(fd,"w");
header(fp, NULL);
fflush(fp);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
execl(prog,prog,NULL);
perror(prog);
}
/* ------------------------------------------------------ *
do_cat(filename,fd)
sends back contents after a header
------------------------------------------------------ */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *content = "text/plain";
FILE *fpsock, *fpfile;
int c;
if ( strcmp(extension,"html") == 0 )
content = "text/html";
else if ( strcmp(extension, "gif") == 0 )
content = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
content = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
content = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
header( fpsock, content );
fprintf(fpsock, "\r\n");
while( (c = getc(fpfile) ) != EOF )
putc(c, fpsock);
fclose(fpfile);
fclose(fpsock);
}
exit(0);
}
[ 本帖最后由 liuke432 于 2006-6-21 16:46 编辑 ]
er 回复于:2006-06-21 15:58:19
支持
kuaizaifeng 回复于:2006-06-21 16:12:56
好东东,支持
cmh_2003 回复于:2006-06-21 17:43:24
先顶下慢慢看~~~
HAMMER 回复于:2006-06-21 17:52:32
新到任的高手啊
linuxcici 回复于:2006-06-21 18:38:16
请问WEB服务器写的时候要注意点什么呢? 小弟正在学. ....好象很多书有关于这web server的code.
liuke432 回复于:2006-06-22 09:27:42
引用:原帖由 HAMMER 于 2006-6-21 17:52 发表
新到任的高手啊
多谢兄弟抬举,我还称不上是什么高手。 :)
学习unix网络编程才半年多,看到有本书上的这个例子比较不错,
对于学习网络编程的朋友很有实践价值。于是就整理了一下,让大
家一起学习。
liuke432 回复于:2006-06-22 09:41:22
引用:原帖由 linuxcici 于 2006-6-21 18:38 发表
请问WEB服务器写的时候要注意点什么呢? 小弟正在学. ....好象很多书有关于这web server的code.
我对这个还不是很精通。 :(
web服务器的侧重点不一样,有的提供了更多的安全特征,有的则快速处理请求或使用较少的内存。
其中一个重要的特征是服务器的效率问题。服务器可以同时处理多少个请求?对于每个请求,服务器需要多少系统资源?
上面那个程序是用多进程来处理的。
下面再贴一个多线程的。
liuke432 回复于:2006-06-22 09:55:46
多线程的web服务器
多线程版本增加了内部统计功能:
服务器的运行时间
接收的客户端请求的数目
发送回客户端的数据量
这里使用独立线程(Detached Threads)防止僵尸线程(Zombie Threads)
/* webserv_pthread.c - a threaded web server
* building: gcc webserv_pthread.c -lpthread -o webserv_pthread
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#define HOSTLEN 256
#define BACKLOG 1
/* server facts here */
time_t server_started ;
int server_bytes_sent;
int server_requests;
main(int ac, char *av[])
{
int sock, fd;
int *fdptr;
pthread_t worker;
pthread_attr_t attr;
void *handle_call(void *);
if ( ac == 1 ){
fprintf(stderr,"usage: tws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) { perror("making socket"); exit(2); }
setup(&attr);
/* main loop here: take call, handle call in new thread */
while(1){
fd = accept( sock, NULL, NULL );
server_requests++;
fdptr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker,&attr,handle_call,fdptr);
}
}
/* ------------------------------------------------------ *
make_server_socket(int portnum)
创建一个服务器套接字,并调用listen监听
------------------------------------------------------ */
int make_server_socket_q(int , int );
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr; /* build our address here */
struct hostent *hp; /* this is part of our */
char hostname[HOSTLEN]; /* address */
int sock_id; /* the socket */
sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */
if ( sock_id == -1 )
return -1;
/** build address and bind it to socket **/
bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */
gethostname(hostname, HOSTLEN); /* where am I ? */
hp = gethostbyname(hostname); /* get info about host */
/* fill in host part */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum); /* fill in socket port */
saddr.sin_family = AF_INET ; /* fill in addr family */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
return -1;
/** arrange for incoming calls **/
if ( listen(sock_id, backlog) != 0 )
return -1;
return sock_id;
}
/*
* initialize the status variables and
* set the thread attribute to detached
*/
setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);
time(&server_started);
server_requests = 0;
server_bytes_sent = 0;
}
void *handle_call(void *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd ;
fd = *(int *)fdptr;
free(fdptr); /* get fd from arg */
fpin = fdopen(fd, "r"); /* buffer input */
fgets(request,BUFSIZ,fpin); /* read client request */
printf("got a call on %d: request = %s", fd, request);
skip_rest_of_header(fpin);
process_rq(request, fd); /* process client rq */
fclose(fpin);
}
/* ------------------------------------------------------ *
skip_rest_of_header(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */
skip_rest_of_header(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
;
}
/* ------------------------------------------------------ *
process_rq( char *rq, int fd )
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */
process_rq( char *rq, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
if ( sscanf(rq, "%s%s", cmd, arg) != 2 )
return;
sanitize(arg);
printf("sanitized version is %s\n", arg);
if ( strcmp(cmd,"GET") != 0 )
not_implemented();
else if ( built_in(arg, fd) )
;
else if ( not_exist( arg ) )
do_404(arg, fd);
else if ( isadir( arg ) )
do_ls( arg, fd );
else
do_cat( arg, fd );
}
/*
* make sure all paths are below the current directory
*/
sanitize(char *str)
{
char *src, *dest;
src = dest = str;
while( *src ){
if( strncmp(src,"/../",4) == 0 )
src += 3;
else if ( strncmp(src,"//",2) == 0 )
src++;
else
*dest++ = *src++;
}
*dest = '\0';
if ( *str == '/' )
strcpy(str,str+1);
if ( str[0]=='\0' || strcmp(str,"./")==0 || strcmp(str,"./..")==0 )
strcpy(str,".");
}
/* handle built-in URLs here. Only one so far is "status" */
built_in(char *arg, int fd)
{
FILE *fp;
if ( strcmp(arg,"status") != 0 )
return 0;
http_reply(fd, &fp, 200, "OK", "text/plain",NULL);
fprintf(fp,"Server started: %s", ctime(&server_started));
fprintf(fp,"Total requests: %d\n", server_requests);
fprintf(fp,"Bytes sent out: %d\n", server_bytes_sent);
fclose(fp);
return 1;
}
http_reply(int fd, FILE **fpp, int code, char *msg, char *type, char *content)
{
FILE *fp = fdopen(fd, "w");
int bytes = 0;
if ( fp != NULL ){
bytes = fprintf(fp,"HTTP/1.0 %d %s\r\n", code, msg);
bytes += fprintf(fp,"Content-type: %s\r\n\r\n", type);
if ( content )
bytes += fprintf(fp,"%s\r\n", content);
}
fflush(fp);
if ( fpp )
*fpp = fp;
else
fclose(fp);
return bytes;
}
/* ------------------------------------------------------ *
simple functions first:
not_implemented(fd) unimplemented HTTP command
and do_404(item,fd) no such object
------------------------------------------------------ */
not_implemented(int fd)
{
http_reply(fd,NULL,501,"Not Implemented","text/plain",
"That command is not implemented");
}
do_404(char *item, int fd)
{
http_reply(fd,NULL,404,"Not Found","text/plain",
"The item you seek is not here");
}
/* ------------------------------------------------------ *
the directory listing section
isadir() uses stat, not_exist() uses stat
------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}
not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}
do_ls(char *dir, int fd)
{
DIR *dirptr;
struct dirent *direntp;
FILE *fp;
int bytes = 0;
bytes = http_reply(fd,&fp,200,"OK","text/plain",NULL);
bytes += fprintf(fp,"Listing of Directory %s\n", dir);
if ( (dirptr = opendir(dir)) != NULL ){
while( direntp = readdir(dirptr) ){
bytes += fprintf(fp, "%s\n", direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}
/* ------------------------------------------------------ *
functions to cat files here.
file_type(filename) returns the 'extension': cat uses it
------------------------------------------------------ */
char * file_type(char *f)
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}
/* do_cat(filename,fd): sends header then the contents */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *type = "text/plain";
FILE *fpsock, *fpfile;
int c;
int bytes = 0;
if ( strcmp(extension,"html") == 0 )
type = "text/html";
else if ( strcmp(extension, "gif") == 0 )
type = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
type = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
type = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
bytes = http_reply(fd,&fpsock,200,"OK",type,NULL);
while( (c = getc(fpfile) ) != EOF ){
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}
作者: 逆雪寒 发布时间: 2007-12-26
多线程的web服务器
多线程版本增加了内部统计功能:
服务器的运行时间
接收的客户端请求的数目
发送回客户端的数据量
这里使用独立线程(Detached Threads)防止僵尸线程(Zombie Threads)
/* webserv_pthread.c - a threaded web server
* building: gcc webserv_pthread.c -lpthread -o webserv_pthread
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#define HOSTLEN 256
#define BACKLOG 1
/* server facts here */
time_t server_started ;
int server_bytes_sent;
int server_requests;
main(int ac, char *av[])
{
int sock, fd;
int *fdptr;
pthread_t worker;
pthread_attr_t attr;
void *handle_call(void *);
if ( ac == 1 ){
fprintf(stderr,"usage: tws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) { perror("making socket"); exit(2); }
setup(&attr);
/* main loop here: take call, handle call in new thread */
while(1){
fd = accept( sock, NULL, NULL );
server_requests++;
fdptr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker,&attr,handle_call,fdptr);
}
}
/* ------------------------------------------------------ *
make_server_socket(int portnum)
创建一个服务器套接字,并调用listen监听
------------------------------------------------------ */
int make_server_socket_q(int , int );
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr; /* build our address here */
struct hostent *hp; /* this is part of our */
char hostname[HOSTLEN]; /* address */
int sock_id; /* the socket */
sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */
if ( sock_id == -1 )
return -1;
/** build address and bind it to socket **/
bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */
gethostname(hostname, HOSTLEN); /* where am I ? */
hp = gethostbyname(hostname); /* get info about host */
/* fill in host part */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum); /* fill in socket port */
saddr.sin_family = AF_INET ; /* fill in addr family */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
return -1;
/** arrange for incoming calls **/
if ( listen(sock_id, backlog) != 0 )
return -1;
return sock_id;
}
/*
* initialize the status variables and
* set the thread attribute to detached
*/
setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);
time(&server_started);
server_requests = 0;
server_bytes_sent = 0;
}
void *handle_call(void *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd ;
fd = *(int *)fdptr;
free(fdptr); /* get fd from arg */
fpin = fdopen(fd, "r"); /* buffer input */
fgets(request,BUFSIZ,fpin); /* read client request */
printf("got a call on %d: request = %s", fd, request);
skip_rest_of_header(fpin);
process_rq(request, fd); /* process client rq */
fclose(fpin);
}
/* ------------------------------------------------------ *
skip_rest_of_header(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */
skip_rest_of_header(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
;
}
/* ------------------------------------------------------ *
process_rq( char *rq, int fd )
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */
process_rq( char *rq, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
if ( sscanf(rq, "%s%s", cmd, arg) != 2 )
return;
sanitize(arg);
printf("sanitized version is %s\n", arg);
if ( strcmp(cmd,"GET") != 0 )
not_implemented();
else if ( built_in(arg, fd) )
;
else if ( not_exist( arg ) )
do_404(arg, fd);
else if ( isadir( arg ) )
do_ls( arg, fd );
else
do_cat( arg, fd );
}
/*
* make sure all paths are below the current directory
*/
sanitize(char *str)
{
char *src, *dest;
src = dest = str;
while( *src ){
if( strncmp(src,"/../",4) == 0 )
src += 3;
else if ( strncmp(src,"//",2) == 0 )
src++;
else
*dest++ = *src++;
}
*dest = '\0';
if ( *str == '/' )
strcpy(str,str+1);
if ( str[0]=='\0' || strcmp(str,"./")==0 || strcmp(str,"./..")==0 )
strcpy(str,".");
}
/* handle built-in URLs here. Only one so far is "status" */
built_in(char *arg, int fd)
{
FILE *fp;
if ( strcmp(arg,"status") != 0 )
return 0;
http_reply(fd, &fp, 200, "OK", "text/plain",NULL);
fprintf(fp,"Server started: %s", ctime(&server_started));
fprintf(fp,"Total requests: %d\n", server_requests);
fprintf(fp,"Bytes sent out: %d\n", server_bytes_sent);
fclose(fp);
return 1;
}
http_reply(int fd, FILE **fpp, int code, char *msg, char *type, char *content)
{
FILE *fp = fdopen(fd, "w");
int bytes = 0;
if ( fp != NULL ){
bytes = fprintf(fp,"HTTP/1.0 %d %s\r\n", code, msg);
bytes += fprintf(fp,"Content-type: %s\r\n\r\n", type);
if ( content )
bytes += fprintf(fp,"%s\r\n", content);
}
fflush(fp);
if ( fpp )
*fpp = fp;
else
fclose(fp);
return bytes;
}
/* ------------------------------------------------------ *
simple functions first:
not_implemented(fd) unimplemented HTTP command
and do_404(item,fd) no such object
------------------------------------------------------ */
not_implemented(int fd)
{
http_reply(fd,NULL,501,"Not Implemented","text/plain",
"That command is not implemented");
}
do_404(char *item, int fd)
{
http_reply(fd,NULL,404,"Not Found","text/plain",
"The item you seek is not here");
}
/* ------------------------------------------------------ *
the directory listing section
isadir() uses stat, not_exist() uses stat
------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}
not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}
do_ls(char *dir, int fd)
{
DIR *dirptr;
struct dirent *direntp;
FILE *fp;
int bytes = 0;
bytes = http_reply(fd,&fp,200,"OK","text/plain",NULL);
bytes += fprintf(fp,"Listing of Directory %s\n", dir);
if ( (dirptr = opendir(dir)) != NULL ){
while( direntp = readdir(dirptr) ){
bytes += fprintf(fp, "%s\n", direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}
/* ------------------------------------------------------ *
functions to cat files here.
file_type(filename) returns the 'extension': cat uses it
------------------------------------------------------ */
char * file_type(char *f)
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}
/* do_cat(filename,fd): sends header then the contents */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *type = "text/plain";
FILE *fpsock, *fpfile;
int c;
int bytes = 0;
if ( strcmp(extension,"html") == 0 )
type = "text/html";
else if ( strcmp(extension, "gif") == 0 )
type = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
type = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
type = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
bytes = http_reply(fd,&fpsock,200,"OK",type,NULL);
while( (c = getc(fpfile) ) != EOF ){
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}
多线程版本增加了内部统计功能:
服务器的运行时间
接收的客户端请求的数目
发送回客户端的数据量
这里使用独立线程(Detached Threads)防止僵尸线程(Zombie Threads)
/* webserv_pthread.c - a threaded web server
* building: gcc webserv_pthread.c -lpthread -o webserv_pthread
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#define HOSTLEN 256
#define BACKLOG 1
/* server facts here */
time_t server_started ;
int server_bytes_sent;
int server_requests;
main(int ac, char *av[])
{
int sock, fd;
int *fdptr;
pthread_t worker;
pthread_attr_t attr;
void *handle_call(void *);
if ( ac == 1 ){
fprintf(stderr,"usage: tws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) { perror("making socket"); exit(2); }
setup(&attr);
/* main loop here: take call, handle call in new thread */
while(1){
fd = accept( sock, NULL, NULL );
server_requests++;
fdptr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker,&attr,handle_call,fdptr);
}
}
/* ------------------------------------------------------ *
make_server_socket(int portnum)
创建一个服务器套接字,并调用listen监听
------------------------------------------------------ */
int make_server_socket_q(int , int );
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr; /* build our address here */
struct hostent *hp; /* this is part of our */
char hostname[HOSTLEN]; /* address */
int sock_id; /* the socket */
sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */
if ( sock_id == -1 )
return -1;
/** build address and bind it to socket **/
bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */
gethostname(hostname, HOSTLEN); /* where am I ? */
hp = gethostbyname(hostname); /* get info about host */
/* fill in host part */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum); /* fill in socket port */
saddr.sin_family = AF_INET ; /* fill in addr family */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
return -1;
/** arrange for incoming calls **/
if ( listen(sock_id, backlog) != 0 )
return -1;
return sock_id;
}
/*
* initialize the status variables and
* set the thread attribute to detached
*/
setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);
time(&server_started);
server_requests = 0;
server_bytes_sent = 0;
}
void *handle_call(void *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd ;
fd = *(int *)fdptr;
free(fdptr); /* get fd from arg */
fpin = fdopen(fd, "r"); /* buffer input */
fgets(request,BUFSIZ,fpin); /* read client request */
printf("got a call on %d: request = %s", fd, request);
skip_rest_of_header(fpin);
process_rq(request, fd); /* process client rq */
fclose(fpin);
}
/* ------------------------------------------------------ *
skip_rest_of_header(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */
skip_rest_of_header(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
;
}
/* ------------------------------------------------------ *
process_rq( char *rq, int fd )
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */
process_rq( char *rq, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
if ( sscanf(rq, "%s%s", cmd, arg) != 2 )
return;
sanitize(arg);
printf("sanitized version is %s\n", arg);
if ( strcmp(cmd,"GET") != 0 )
not_implemented();
else if ( built_in(arg, fd) )
;
else if ( not_exist( arg ) )
do_404(arg, fd);
else if ( isadir( arg ) )
do_ls( arg, fd );
else
do_cat( arg, fd );
}
/*
* make sure all paths are below the current directory
*/
sanitize(char *str)
{
char *src, *dest;
src = dest = str;
while( *src ){
if( strncmp(src,"/../",4) == 0 )
src += 3;
else if ( strncmp(src,"//",2) == 0 )
src++;
else
*dest++ = *src++;
}
*dest = '\0';
if ( *str == '/' )
strcpy(str,str+1);
if ( str[0]=='\0' || strcmp(str,"./")==0 || strcmp(str,"./..")==0 )
strcpy(str,".");
}
/* handle built-in URLs here. Only one so far is "status" */
built_in(char *arg, int fd)
{
FILE *fp;
if ( strcmp(arg,"status") != 0 )
return 0;
http_reply(fd, &fp, 200, "OK", "text/plain",NULL);
fprintf(fp,"Server started: %s", ctime(&server_started));
fprintf(fp,"Total requests: %d\n", server_requests);
fprintf(fp,"Bytes sent out: %d\n", server_bytes_sent);
fclose(fp);
return 1;
}
http_reply(int fd, FILE **fpp, int code, char *msg, char *type, char *content)
{
FILE *fp = fdopen(fd, "w");
int bytes = 0;
if ( fp != NULL ){
bytes = fprintf(fp,"HTTP/1.0 %d %s\r\n", code, msg);
bytes += fprintf(fp,"Content-type: %s\r\n\r\n", type);
if ( content )
bytes += fprintf(fp,"%s\r\n", content);
}
fflush(fp);
if ( fpp )
*fpp = fp;
else
fclose(fp);
return bytes;
}
/* ------------------------------------------------------ *
simple functions first:
not_implemented(fd) unimplemented HTTP command
and do_404(item,fd) no such object
------------------------------------------------------ */
not_implemented(int fd)
{
http_reply(fd,NULL,501,"Not Implemented","text/plain",
"That command is not implemented");
}
do_404(char *item, int fd)
{
http_reply(fd,NULL,404,"Not Found","text/plain",
"The item you seek is not here");
}
/* ------------------------------------------------------ *
the directory listing section
isadir() uses stat, not_exist() uses stat
------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}
not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}
do_ls(char *dir, int fd)
{
DIR *dirptr;
struct dirent *direntp;
FILE *fp;
int bytes = 0;
bytes = http_reply(fd,&fp,200,"OK","text/plain",NULL);
bytes += fprintf(fp,"Listing of Directory %s\n", dir);
if ( (dirptr = opendir(dir)) != NULL ){
while( direntp = readdir(dirptr) ){
bytes += fprintf(fp, "%s\n", direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}
/* ------------------------------------------------------ *
functions to cat files here.
file_type(filename) returns the 'extension': cat uses it
------------------------------------------------------ */
char * file_type(char *f)
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}
/* do_cat(filename,fd): sends header then the contents */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *type = "text/plain";
FILE *fpsock, *fpfile;
int c;
int bytes = 0;
if ( strcmp(extension,"html") == 0 )
type = "text/html";
else if ( strcmp(extension, "gif") == 0 )
type = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
type = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
type = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
bytes = http_reply(fd,&fpsock,200,"OK",type,NULL);
while( (c = getc(fpfile) ) != EOF ){
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}
作者: 逆雪寒 发布时间: 2007-12-26
楼主汗啊!~~~
作者: forest 发布时间: 2007-12-26
嗯,还是不错的说。

作者: luzhou 发布时间: 2007-12-26

作者: xjgale 发布时间: 2007-12-27
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28