有关自我实现的名言(实现自己的http server loop_in_codes C++博客)
实现自己的http server
Write your own http server
author : Kevin Lynx
Why write your own?
看这个问题的人证明你知道什么是http server ,世界上有很多各种规模的http server ,为什么要自己实现一个?其实没什么
理由 。我自己问自己 ,感觉就是在自己娱乐自己 ,或者说只是练习下网络编程 ,或者是因为某日我看到某个库宣称自己附带一个小
型的http server时 ,我不知道是什么东西 ,于是就想自己去实现一个 。
Whats httpd ?
httpd就是http daemon ,这个是类unix系统上的名称 ,也就是http server 。httpd遵循HTTP协议 ,响应HTTP客户端的request ,
然后返回response 。
那么 ,什么是HTTP协议?最简单的例子,就是你的浏览器与网页服务器之间使用的应用层协议 。虽然官方文档说HTTP协议可以
建立在任何可靠传输的协议之上 ,但是就我们所见到的 ,HTTP还是建立在TCP之上的 。
httpd最简单的response是返回静态的HTML页面 。在这里我们的目标也只是一个响应静态网页的httpd而已(也许你愿意加入CGI
特性) 。More details about HTTP protocol
在这里有必要讲解HTTP协议的更多细节,因为我们的httpd就是要去解析这个协议 。
关于HTTP协议的详细文档 ,可以参看rfc2616 。但事实上对于实现一个简单的响应静态网页的httpd来说 ,完全没必要读这么一
分冗长的文档 。在这里我推荐<HTTP Made Really Easy> ,以下内容基本取自于本文档 。- HTTP协议结构
HTTP协议无论是请求报文(request message)还是回应报文(response message)都分为四部分:
* 报文头 (initial line )
* 0个或多个header line
* 空行(作为header lines的结束)
* 可选body
HTTP协议是基于行的协议 ,每一行以\r\n作为分隔符。报文头通常表明报文的类型(例如请求类型) ,报文头只占一行;header line
附带一些特殊信息 ,每一个header line占一行 ,其格式为name:value ,即以分号作为分隔;空行也就是一个\r\n;可选body通常
包含数据 ,例如服务器返回的某个静态HTML文件的内容 。举个例子 ,以下是一个很常见的请求报文 ,你可以截获浏览器发送的数据
包而获得:1 GET /index.html HTTP/1.1
2 Accept-Language: zh-cn
3 Accept-Encoding: gzip, deflate
4 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; MAXTHON 2.0)
5 Host: localhost
6 Connection: Keep-Alive
7
我为每一行都添加了行号 ,第1行就是initial line,2-6行是header lines ,7行是一个header line的结束符 ,没有显示出来 。
以下是一个回应报文:
1 HTTP/1.1 200 OK
2 Server: klhttpd/0.1.0
3 Content-Type: text/html
4 Content-Length: 67
5
6
- HTTP request method
因为我们做的事服务器端 ,所以我们重点对请求报文做说明 。首先看initial line ,该行包含几个字段 ,每个字段用空格分开 ,例
如以上的GET /index.html HTTP/1.1就可以分为三部分:GET 、/index.html 、HTTP/1.1 。其中第一个字段GET就是所谓的request
method 。它表明请求类型 ,HTTP有很多method ,例如:GET 、POST 、HEAD等 。就我们的目标而言 ,我们只需要实现对GET和HEAD做响应即可 。
GET是最普遍的method ,表示请求一个资源 。什么是资源?诸如HTML网页 、图片 、声音文件等都是资源 。顺便提一句 ,HTTP协议
中为每一个资源设置一个唯一的标识符 ,就是所谓的URI(更宽泛的URL) 。
HEAD与GET一样 ,不过它不请求资源内容 ,而是请求资源信息,例如文件长度等信息 。- More detail
继续说说initial line后面的内容:
对应于GET和HEAD两个method ,紧接着的字段就是资源名 ,其实从这里可以看出,也就是文件名(相对于你服务器的资源目录) ,例
如这里的/index.html;最后一个字段表明HTTP协议版本号 。目前我们只需要支持HTTP1.1和1.0 ,没有多大的技术差别 。然后是header line 。我们并不需要关注每一个header line。我只罗列有用的header line :
- Host : 对于HTTP1.1而言 ,请求报文中必须包含此header ,如果没有包含 ,服务器需要返回bad request错误信息 。
- Date : 用于回应报文 ,用于客户端缓存数据用 。
- Content-Type : 用于回应报文 ,表示回应资源的文件类型 ,以MIME形式给出。什么是MIME?它们都有自己的格式 ,例如:
text/html, image/jpg, image/gif等 。
- Content-Length : 用于回应报文 ,表示回应资源的文件长度 。body域很简单 ,你只需要将一个文件全部读入内存 ,然后附加到回应报文段后发送即可,即使是二进制数据 。
- 回应报文
之前提到的一个回应报文例子很典型 ,我们以其为例讲解 。首先是initial line ,第一个字段表明HTTP协议版本,可以直接以请求
报文为准(即请求报文版本是多少这里就是多少);第二个字段是一个status code ,也就是回应状态 ,相当于请求结果 ,请求结果
被HTTP官方事先定义 ,例如200表示成功 、404表示资源不存在等;最后一个字段为status code的可读字符串 ,你随便给吧 。回应报文中最好跟上Content-Type 、Content-Length等header 。
具体实现
正式写代码之前我希望你能明白HTTP协议的这种请求/回应模式 ,即客户端发出一个请求 ,然后服务器端回应该请求 。然后继续
这个过程(HTTP1.1是长连接模式 ,而HTTP1.0是短连接 ,当服务器端返回第一个请求时 ,连接就断开了) 。
这里 ,我们无论客户端 ,例如浏览器,发出什么样的请求 ,请求什么资源 ,我们都回应相同的数据:程序以最简单的阻塞模式运行,我们可以将重点放在协议的分析上 。运行程序 ,在浏览器里输入http://localhost:8080/index.html
,然后就可以看到浏览器正常显示content中描述的HTML文件 。假设程序在8080端口监听 。现在你基本上明白了整个工作过程 ,我们可以把代码写得更全面一点 ,例如根据GET的URI来载入对应的文件然后回应给客户端 。
其实这个很简单 ,只需要从initial line里解析出(很一般的字符串解析)URI字段 ,然后载入对应的文件即可。例如以下函数:
request)
{
/**//*getthemethod*/char*token=strtok(request,"");
char*uri=strtok(0,"");
charfile[64];
sprintf(file, ".%s",uri);
{
/**//*loadthefilecontent*/FILE*fp=fopen(file,"rb");
if(fp==0)
{
/**//*response404statuscode*/charresponse[]="HTTP/1.1404NOTFOUND\r\n\r\n";
send(con,response,strlen(response), 0);
} else{
/**//*responsetheresource*//**//*first,loadthefile*/intfile_size;
char*content;
charresponse[1024];
fseek(fp, 0,SEEK_END);
file_size =ftell(fp);
fseek(fp, 0,SEEK_SET);
content =(char*)malloc(file_size+1);
fread(content,file_size, 1,fp);
content[file_size] =0;
sprintf(response, "HTTP/1.1200OK\r\nContent-Type:text/html\r\nContent-Length:%d\r\n\r\n%s",file_size,content);
send(con,response,strlen(response), 0);
free(content);
} }}其他
要将这个简易的httpd做完善 ,我们还需要注意很多细节 。包括:对不支持的method返回501错误;对于HTTP1.1要求有Host这个
header;为了支持客户端cache ,需要添加Date header;支持HEAD请求等 。相关下载中我提供了一个完整的httpd library ,纯C的代码 ,在其上加上一层资源载入即可实现一个简单的httpd。在这里我将
对代码做简要的说明:
evbuffer.h/buffer.c : 取自libevent的buffer ,用于缓存数据;
klhttp-internal.h/klhttp-internal.c :主要用于处理/解析HTTP请求 ,以及创建回应报文;
klhttp-netbase.h/klhttp-netbase.c :对socket api的一个简要封装,使用select模型;
klhttp.h/klhttp.c :库的最上层 ,应用层主要与该层交互 ,这一层主要集合internal和netbase 。
test_klhttp.c :一个测试例子 。相关下载:
klhttpd
文中相关代码参考资料:
http://www.w3.org/Protocols/rfc2616/rfc2616.html
http://jmarshall.com/easy/http/
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!