原创 [原创]自己动手写 HTTP Server收藏

自己动手写 HTTP Server

【 原理 】 一般来说,HTTP Server 也是我们常说的Web服务器,大名鼎鼎的 Apache,还是微软的 IIS (Internet Information Server),开源领域的有 Lighttpd 和最近风头正劲的 Nginx 都是典型的Web服务器。最近想想能不能做一个Web服务器,就提供简单的功能,但是速度很快,能够作为一个专门处理 HTML/CSS/JS/Image 的Web服务器,那样能够让静态资源文件迅速的被访问到,如果有反向代理功能就更帅了,当然了,要是有Cache功能啥的,并且能够编写自定义插件(扩展)就很完美了。。。YY中。。。

基于这个思想,我就花十天时间使用标准C写了一个千行代码小型的HTTP Server,当然目前还不具有反向代理和扩展功能,只是能够简单的支持 HTML/CSS/JS/Image 静态资源文件的访问。HTTP Server 的名字叫做 tmhttpd - TieMa (Tiny&Mini) HTTP Server,小巧,代码少,执行速度快,目前具有的功能包括:  

  •  Support GET/HEAD method
  •  The common MIME types.
  •  Support Self custom default index page
  •  Directory listings.
  •  Support access log
  •  Support Self custom port and max clients
  •  Use fork mode accept new conntion
  •  Support daemon type work
  • more ..

目前已经发布了 tmhttpd-1.0.0_alpha 版本,包括for Unix/Linux (在 Ubuntu/Fedoar/FreeBSD 下编译通过) 和 在cygwin环境下编译的 for Windows 版本,下面地址有下载:

下面大致来聊聊怎么写一个 HTTP Server,先看看一个HTTP请求的流程:

HTTP Server process

大致就是 客户端的浏览器(IE、Firefox、Opera、Lynx、Curl、Robot ...) 访问到Web服务器的端口(一般是80),然后端口接受到请求后开始解析请求,如果请求不正确或者非法则直接返回指定的错误代码(可以参考 RFC 1945),如果请求合法,那么就查找相应用户请求的文件,把文件读取后发送给客户端,关闭连接,请求结束。

【 实现 】

贴部分tmhttpd核心代码来描述这个过程:

/**

 * 初始化服务器端的Socket连接,等待连接,连接成功后fork新进程处理

 *

 */

static void InitServerListen( unsigned int port, unsigned int max_client ){

    int serversock, clientsock;

    struct sockaddr_in server_addr, client_addr;

	char currtime[32];



    /* Create the TCP socket */

    if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){

        die("Failed to create socket");

    }



    /* Construct the server sockaddr_in structure */

    memset(&server_addr, 0, sizeof(server_addr));       /* Clear struct */

    server_addr.sin_family = AF_INET;                  /* Internet/IP */

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* Incoming addr */

    server_addr.sin_port = htons(port);          /* server port */



    /* Bind the server socket */

    if (bind(serversock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0){

        die("Failed to bind the server socket");

    }

    /* Listen on the server socket */

    if (listen(serversock, max_client) < 0){

        die("Failed to listen on server socket");

    }



	/* Print listening message */

	getdate(currtime);

	fprintf(stdout, "[%s] Start server listening at port %d ...\n", currtime, port);

	fprintf(stdout, "[%s] Waiting client connection ...\n", currtime);





    /* Run until cancelled */

    while (1){

        unsigned int clientlen = sizeof(client_addr);

		memset(currtime, 0, sizeof(currtime));

		getdate(currtime);



        /* Wait for client connection */

        if ((clientsock = accept(serversock, (struct sockaddr *) &client_addr, &clientlen)) < 0){

            die("Failed to accept client connection");

        }



        /* Use child process new connection */

        if ( fork() == 0 ){

			HandleClient(clientsock, client_addr);

		} else {

			wait(NULL); 

		}



		/* Not use close socket connection */

        close(clientsock);

    }



}







/**

 * 获取一个连接,读取连接客户端发送的请求数据,把请求数据叫给请求解析函数进行解析

 *

 */

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);

}





/**

 * 解析一个请求,解析出GET/HEAD方法,需要请求的文件,协议版本等,构造成结构体提交给处理函数

 *

 */

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;

	struct st_request_info request_info;



	/* Split client request */

	getdate(currtime);

	explode(req, '\n', &buf, &line_total);



	/* Print log message  */

	memset(log, 0, sizeof(log));

	sprintf(log, "[%s] %s %s\n", currtime, inet_ntoa(client_addr.sin_addr), buf[0]);

	WriteLog(log);



	/* Check request is empty */

	if (strcmp(buf[0], "\n") == 0 || strcmp(buf[0], "\r\n") == 0){

		SendError( client_sock, 400, "Bad Request", "", "Can't parse request." );

	}



	/* Check method is implement */

	explode(buf[0], ' ', &method_buf, &method_total);

	if ( strcmp( strtolower(method_buf[0]), "get") != 0 &&  strcmp( strtolower(method_buf[0]), "head") != 0 ){

		SendError( client_sock, 501, "Not Implemented", "", "That method is not implemented." );

	}

	explode(method_buf[1], '?', &query_buf, &query_total);



	/* Make request data */	

	getcwd(cwd, sizeof(cwd));

	strcpy(pathinfo, query_buf[0]);

	substr(query_buf[0], 0, strrpos(pathinfo, '/')+1, path);

	substr(query_buf[0], strrpos(pathinfo, '/')+1, 0, file);

	

	/* Pad request struct */

	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;



	/* Is a directory pad default index page */

	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;

		}

	}

	

	/* Debug message */

	if ( g_is_debug ){

		fprintf(stderr, "[ Request ]\n");

		for(i=0; i<line_total; i++){

			fprintf(stderr, "%s\n", buf[i]);

		}

	}



	/* Process client request */

	ProcRequest( client_sock, client_addr, request_info );



	return 0;

}





/**

 * 处理函数按照解析出来的请求内容进行数据返回,返回文件/目录列表或者提示错误

 *

 */

static int ProcRequest( int client_sock, struct sockaddr_in client_addr, struct st_request_info request_info ){

	char buf[128];



	/* File is exist or has access permission */

	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);

	}



	/* Check target is regular file or directory */

	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 ){ 

		/* Is a directory choose browse dir list */

		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;

}

  主要核心的函数就是这四个,如果要了解其他函数,包括字符串处理,HTTP头信息发送,错误发送,读取文件,遍历目录等等请下载源码回去研究。源码下载地址:http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha.tar.gz

【 结束 】

其实还有很多功能能够增加的,比如性能,采用 select 或者 epool 肯定要比单纯的fork性能更好,另外增加处理动态内容,比如CGI,能够接受POST请求,能够作为一个反向代理存在,能够具有缓存功能,能够缓存请求过的文件到内存中,能够具有插件扩展功能等等,都是需要进一步提升的。

参考文档:  

发表于 @ 2008年06月22日 23:02:00|评论(loading...)|举报|收藏

新一篇: [转] KFS,一个克隆GFS的文件系统 | 旧一篇: [转]Yupoo! 的网站技术架构

用户操作
[即时聊天] [发私信] [加为好友]
heiyeluren
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
heiyeluren的公告

联系方式:


访问统计: free hit counter code
FeedSky订阅:
FeedSky订阅
文章分类
收藏
    ::eYou::
    kevin world
    lewis - 老吕
    qyb - BT的花
    Realzay的blog
    叶金荣
    天堂地狱鬼-dulao5's Blog
    沙漠之周
    狐狸糊涂
    老韩
    與子觀化
    鞋门
    ::Yahoo::
    glemir’s blog
    happy_fish - 分布式文件系统FastDFS
    LinZi's Blog
    Rainx
    stauren
    互联网,请记住我 - 162同学的技术博客
    冰的河
    北冥之鱼-张彪同学
    天韵之星
    小蚂蚁同学滴测试博客
    随网之舞 - kaven的DHTML博客
    风雪之隅
    ::朋友::
    【推荐】中文分类网
    DDR的博客
    kevin world
    miky
    PHPCup.cn论坛
    俺兄弟的blog
    冰河的技术博客:心随风动
    好旅网
    小少的技术博客
    无尘居
    晋陵路人的Blog
    李天华同学滴技术博客
    沙狐部落
    轻量级的editor
    ::网友::
    blankyao同学
    Code & Stock.
    Hello, Willko
    LionD8的Blog
    magiclab.cn
    MooPHP - 轻量级PHP框架
    Phzzy
    Xhttpd.cn
    张贺同学的博客
    技术大牛老余的博客
    抚琴居
    旋木木同学滴博客
    流水孟春
    矛盾网
    程序人生
    邢红瑞的blog
    阿健的博客
    :PHP博客:
    .: Easy style :.
    [琴剑楼]
    CoolCode.cn
    Haohappy的Blog
    Hightman
    iwind的blog
    Javascript开发站
    JD Space
    Nio's Weblog
    Open Source PHP
    PHP面对对象
    SourceForge.net
    trip的专栏
    UGIA.cn
    windix's blog
    Windix's Weblog
    一个藏袍
    俊麟 Michael`s blog
    偶然的blog
    刘敏的blog
    大龄青年的Blog
    廖宇雷的blog
    懒猫开始新生活blog
    某人的栖息地
    王春生的博客
    神仙
    :牛人blog:
    DBA notes
    http://blog.csdn.net/tingya/
    侯捷网站
    孟岩
    搜索引擎研究
    方舟
    王咏刚的BLOG
    竹笋炒肉
    荣耀
    车东[Blog^2]
    透明思考
    陈硕的Blog
    DHTML
    DHTMLGoodies
    FCKEditor
    Google Code
    Google Web Toolkit
    HTML Goodies
    HTML.it
    HTMLAre
    HTMLdog
    JavaScript Kit
    jQuery
    KindEditor
    Prototype
    TinyMCE
    W3 Schools
    Yahoo JavaScript Developer Center
    Yahoo! Developer Network
    Yahoo! UI Library (YUI)
    网页设计师Web标准
    Java国内站
    ChinaJavaWorld.com技术论坛
    IBM developerWorks 中国: Java
    Java中文站
    Java开源大全
    Java爱好者
    JR - Java翻译站
    J道-JDON
    Matrix: 与Java共舞
    中国Java开发网
    中文java技术网
    PHP国内站点
    CSDN PHP论坛
    Discuz!
    FleaPHP
    Google--PHP用户组
    IBM DeveloperWorks
    JavsScript技术讨论
    Nirvana Studio
    OpenPHP.cn
    PHPChina
    TiM Club
    中文 PFC 1.0 手册--PHP5的开发包
    中文 PFC 1.0 手册--PHP5的开发包
    中文PHP网
    太平洋--PHP开发区
    爱MySQL
    超越PHP
    PHP国外站点
    ADOdb
    Agavi Framework
    Cake PHP
    MySQL Performance Blog
    MySQL Performance Blog
    Nonaweb
    PEAR
    PECL
    PECL Windows
    PHP Builder
    PHP Classes
    PHP Classes
    PHP New Download
    PHP Security Consortium
    php.MVC
    php.MVC
    PHPkitchen(OO & MVC)
    phpPatterns
    PHP国外图书下载
    smart template
    Smarty
    SourceForge.net
    Symfony Framework
    Zend
    Zend Framework
    Unix C/C++
    Free Gentux
    周立发的blog(Linux C)
    Unix/Linux
    BSD智库
    ChinaUnix
    FreeBSDChina
    FreeLAMP
    IBM开发者Linux专区
    Linux Byte
    LinuxKit
    LinuxTS
    Linux伊甸园
    Linux技术中坚站
    Linux非常空间
    Love Unix
    NetBSD&OpenBSD中文用户组
    NetBSD中国社区
    Oracle中国用户讨论组
    OurLinux
    Unix中文
    Unix中文
    Unix中文宝库
    中国Linux公社
    中国Unix用户技术论坛
    中文FreeBSD用户组
    永远的Unix
    炎黄角马
    程序设计
    CSDN
    IBM开发者中心
    Microsoft TechNet: 主页
    MSDN 中文网站
    PHP中文站
    Sun技术社区
    中国IT认证实验室--企业应用技术
    中国协议分析网
    喜悦国际村
    太平洋电脑网---开发特区
    实用网站
    veBook(国外大量免费图书下载网站)
    Whois.net
    中国Web信息博物馆
    中国互联网络信息中心whois查询
    服务器系统信息查看
    网络安全
    AnySide.com
    CGI Secutiry
    K-OTik Security Monitoring
    Linux Security
    Packet Storm Security
    PHP Secure
    RFC中文文档索引
    Safemode.org
    SecuriTeam.com
    Security Corporation
    SecurityFocus
    SecurityTracker
    Zone-h (区域黑客,每天公布各国被黑的网站)
    中华安全网
    中国信息安全组织
    国家计算机网络应急处理中心
    安全天使
    安全焦点
    幻影旅团
    绿盟科技
    网络安全评估中心(cnns )
    在线手册
    Apache2.0中文文档
    Beyond Linux From Scratch
    Debian参考手册
    FreeBSD Porter 手册
    FreeBSD使用手册
    Linux C函数中文参考手册
    MySQL 4.1.0 中文参考手册
    NetBSD在线手册
    OpenBSD在线FAQ
    PHP ADODB 1.99版手册中文翻译(Tripc)
    PHP中文手册(国内)
    PHP中文手册(国外)
    PostgreSQL中文文档
    Red Hat Linux 9入门指南
    Red Hat Linux 9安装指南
    Red Hat Linux 9定制手册
    中国OSS技术手册中心
    技术文档手册中心-ChinaUnix
    存档
    Csdn Blog version 3.1a
    Copyright © heiyeluren