C语言写的简易实用的web服务器

码农公社  210.net.cn   210是何含义?10月24日是程序员节,1024 = 210、210既 210 之意。

Apache在码农界是比较知名的,它也是目前最接地气、使用最广泛的Web服务器。大家可以从news.netcraft.com/这个网站得到证实。 

Apache在功能、效率、开源三个方面对我很有吸引力,但囿于自己的技术水平,无法从Apache庞大的source code里面理清头绪。

懒惰中,冒出自己动手写一个简易实用的Web服务器的主意,在此分享给大家,权当抛砖引玉!


我的实验环境为:     

OS : Red Hat Enterprise Linux 5  

gcc : 4.1.2  

libc : 2.5  

editor : Vim  

lang : C 


阅读该源代码需要以下预备知识:  

C语言基础  

Linux编程基础  

socket编程基础(Linux)  

TCP/IP基本原理  

HTTP基本原理  


下面是第一个版本(0.1 Alpha),实现了WEB 服务器的最基本功能 ,包括以下源文件:  

webserver.c----程序入口  

init_socket.h 

init_socket.c----完成一些WEB服务器的初始化工作  

get_time.h get_time.c----获得服务器的时间  

http_session.h http_session.c----处理一次HTTP会话  


以下是各文件源码:  


webserver.c

/*

* file:webserver.c

*/

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<strings.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include"get_time.h"

#include"init_socket.h"

#include"http_session.h"

intmain(intargc,char*argv[])

{

intlisten_fd;

intconnect_fd;

structsockaddr_inserver_addr;

structsockaddr_inclient_addr;

bzero(&server_addr,sizeof(structsockaddr_in));

bzero(&client_addr,sizeof(structsockaddr_in));

if(init_socket(&listen_fd,&server_addr)==-1)

{

perror("init_socket() error. in webserver.c");

exit(EXIT_FAILURE);

}

socklen_taddrlen=sizeof(structsockaddr_in);

pid_tpid;

while(1)

{

if((connect_fd=accept(listen_fd,(structsockaddr*)&client_addr,&addrlen))==-1)

{

perror("accept() error. in webserver.c");

continue;

}

if((pid=fork())>0)

{

close(connect_fd);

continue;

}

elseif(pid==0)

{

close(listen_fd);

printf("pid %d process http session from %s : %d
",getpid(),inet_ntoa(client_addr.sin_addr),htons(client_addr.sin_port));

if(http_session(&connect_fd,&client_addr)==-1)

{

perror("http_session() error. in webserver.c");

shutdown(connect_fd,SHUT_RDWR);

printf("pid %d loss connection to %s
",getpid(),inet_ntoa(client_addr.sin_addr));

exit(EXIT_FAILURE);/* exit from child process, stop this http session  */

}

printf("pid %d close connection to %s
",getpid(),inet_ntoa(client_addr.sin_addr));

shutdown(connect_fd,SHUT_RDWR);

exit(EXIT_SUCCESS);

}

else

{

perror("fork() error. in webserver.c");

exit(EXIT_FAILURE);

}

}

shutdown(listen_fd,SHUT_RDWR);

return0;

}



init_socket.h

/*

* file:init_socket.h

*/

#ifndefINIT_SOCKET_H

#defineINIT_SOCKET_H

#include<netinet/in.h>

#defineBACKLOG    20/* length of listening queue on socket */

#definePORT    8080/* web server listening port */

/* initialize the socket on server, include below

socket();

bind();

listen();

*/

/* listen_fd : the web server listen file decriptor

server_addr: the web server ipv4 address

RETURNS: success on 0, error on -1

*/

intinit_socket(int*listen_fd,structsockaddr_in*server_addr);

#endif



init_socket.c


/*

* file:init_socket.c

*/

#include<stdio.h>

#include<strings.h>

#include<unistd.h>

#include<netinet/in.h>

#include<sys/types.h>

#include<sys/socket.h>

#include"init_socket.h"

intinit_socket(int*listen_fd,structsockaddr_in*server_addr)

{

if((*listen_fd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

perror("socket() error. in init_socket.c");

return-1;

}

/* set reuse the port on server machine  */

intopt=SO_REUSEADDR;

if(setsockopt(*listen_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))==-1)

{

perror("setsockopt() error.  in init_socket.c");

return-1;

}

server_addr->sin_family=AF_INET;

server_addr->sin_port=htons(PORT);

server_addr->sin_addr.s_addr=htonl(INADDR_ANY);

if(bind(*listen_fd,(structsockaddr*)server_addr,sizeof(structsockaddr_in))==-1)

{

perror("bind() error.  in init_socket.c");

return-1;

}

if(listen(*listen_fd,BACKLOG)==-1)

{

perror("listen() error.  in init_socket.c");

return-1;

}

return0;

}



get_time.h


/*

* file: get_time.h

*/

#ifndefGET_TIME_H

#defineGET_TIME_H

#defineTIME_BUFFER_SIZE    40/* buffer size of time_buffer  */

char*get_time_str(char*time_buf);

#endif



get_time.c


/*

* file:get_time.c

*/

#include<time.h>

#include<stdio.h>

#include<string.h>

#include"get_time.h"

/* get the time on server,

return: the ascii string of time , NULL on error

argument: time_buf the buffer to store time_string

*/

char*get_time_str(char*time_buf)

{

time_tnow_sec;

structtm*time_now;

if(time(&now_sec)==-1)

{

perror("time() in get_time.c");

returnNULL;

}

if((time_now=gmtime(&now_sec))==NULL)

{

perror("localtime in get_time.c");

returnNULL;

}

char*str_ptr=NULL;

if((str_ptr=asctime(time_now))==NULL)

{

perror("asctime in get_time.c");

returnNULL;

}

strcat(time_buf,str_ptr);

returntime_buf;

}



http_session.c


/*

* file: http_session.h

*/

#ifndefHTTP_SESSION_H

#defineHTTP_SESSION_H

#include<netinet/in.h>

#defineRECV_BUFFER_SIZE    1024/* 1KB of receive buffer  */

#defineSEND_BUFFER_SIZE    1050000/* 1.xMB of send buffer  */

#defineURI_SIZE            128/* length of uri request from client browse */

#defineTIME_OUT_SEC        10/* select timeout of secend */

#defineTIME_OUT_USEC        0/* select timeout of usecend */

 

#defineFILE_OK                200

#defineFILE_FORBIDEN        403/* there are no access permission*/

#defineFILE_NOT_FOUND        404/* file not found on server */

#defineUNALLOW_METHOD        405/* un allow http request method*/

#defineFILE_TOO_LARGE        413/* file is too large */

#defineURI_TOO_LONG        414/*  */

#defineUNSUPPORT_MIME_TYPE    415

#defineUNSUPPORT_HTTP_VERSION    505

#defineFILE_MAX_SIZE        1048576/* 1MB the max siee of file read from hard disk */

#defineALLOW"Allow:GET"/* the server allow GET request method*/

#defineSERVER"Server:Mutu(0.1 Alpha)/Linux"

/* if the connect protocol is http then this function deal with it  */

inthttp_session(int*connect_fd,structsockaddr_in*client_addr);

/* if http protocol return 1, else return 0 */

intis_http_protocol(char*msg_from_client);

/* get the request header's uri */

char*get_uri(char*req_header,char*uri_buf);

/* get the uri status,access return 0, not exist return 1, permission deny return 2, error return -1 */

intget_uri_status(char*uri);

/* get the mime type of the file request in uri from client's browse */

char*get_mime_type(char*uri);

/* read the file which requested by client in uri ,and store in entity_buf.

success return bytes readed,error return -1

*/

intget_file_disk(char*uri,unsignedchar*entity_buf);

/* set http replay header's status:

200:ok

404:file not found

*/

intset_rep_status();

intset_error_information(unsignedchar*send_buf,interrorno);

intreply_normal_information(unsignedchar*send_buf,unsignedchar*file_buf,intfile_size,char*mime_type);

#endif



如何访问该服务器?  


通过在浏览器地址栏输入 http://xxx.xxx.xxx.xxx:8080  来访问该web服务器。

xxx.xxx.xxx.xxx 指的是ip地址。

如果你在本机进行测试,IP地址可以直接用127.0.0.1(回环地址,localhost) 。 

如果你在服务器进行测试,请替换成具体ip地址  。

评论