Write your own http server

author : Kevin Lynx

Why write your own?

    看这个问题的人证明你知道什么是http server,世界上有很多各种规模的http server,为什么要自己实现一个?其实没什么
理由。我自己问自己,感觉就是在自己娱乐自己,或者说只是练习下网络编程,或者是因为某日我看到某个库宣称自己附带一个小
型的http server时,我不知道是什么东西,于是就想自己去实现一个。 What's 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文件的内容。举个例子,以下是一个很常见的请求报文,你可以截获浏览器发送的数据
包而获得: GET /index.html HTTP/1.1
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.; MAXTHON 2.0)
Host: localhost
Connection: Keep-Alive 我为每一行都添加了行号,第1行就是initial line,-6行是header lines,7行是一个header line的结束符,没有显示出来。
以下是一个回应报文:
HTTP/1.1 OK
Server: klhttpd/0.1.
Content-Type: text/html
Content-Length: <head><head><title>index.html</title></head><body>index.html</body>
第6行就是可选的body,这里是index.html这个文件的内容。 - HTTP request method
因为我们做的事服务器端,所以我们重点对请求报文做说明。首先看initial line,该行包含几个字段,每个字段用空格分开,例
如以上的GET /index.html HTTP/.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.,没有多大的技术差别。 然后是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是短连接,当服务器端返回第一个请求时,连接就断开了)。
这里,我们无论客户端,例如浏览器,发出什么样的请求,请求什么资源,我们都回应相同的数据: /* 阻塞地接受一个客户端连接 */
SOCKET con = accept( s, , );
/* recv request */
char request[] = { };
ret = recv( con, request, sizeof( request ), );
printf( request );
/* whatever we recv, we send 200 response */
{
char content[] = "<head><head><title>index.html</title></head><body>index.html</body>";
char response[];
sprintf( response, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n%s", strlen( content ), content );
ret = send( con, response, strlen( response ), );
}
closesocket( con ); 程序以最简单的阻塞模式运行,我们可以将重点放在协议的分析上。运行程序,在浏览器里输入http://localhost:8080/index.html
,然后就可以看到浏览器正常显示content中描述的HTML文件。假设程序在8080端口监听。 现在你基本上明白了整个工作过程,我们可以把代码写得更全面一点,例如根据GET的URI来载入对应的文件然后回应给客户端。
其实这个很简单,只需要从initial line里解析出(很一般的字符串解析)URI字段,然后载入对应的文件即可。例如以下函数: void http_response( SOCKET con, const char *request )
{
/* get the method */
char *token = strtok( request, " " );
char *uri = strtok( , " " );
char file[];
sprintf( file, ".%s", uri ); {
/* load the file content */
FILE *fp = fopen( file, "rb" );
if( fp == )
{
/* response 404 status code */
char response[] = "HTTP/1.1 404 NOT FOUND\r\n\r\n";
send( con, response, strlen( response ), );
}
else
{
/* response the resource */
/* first, load the file */
int file_size ;
char *content;
char response[];
fseek( fp, , SEEK_END );
file_size = ftell( fp );
fseek( fp, , SEEK_SET );
content = (char*)malloc( file_size + );
fread( content, file_size, , fp );
content[file_size] = ; sprintf( response, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n%s", file_size, content );
send( con, response, strlen( response ), );
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 :一个测试例子。

如何实现HTTPSERVER的更多相关文章

  1. NodeJS 最快速搭建一个HttpServer

    最快速搭建一个HttpServer 在目录里放一个index.html cd D:\Web\InternalWeb start http-server -i -p 8081

  2. 基于Netty4的HttpServer和HttpClient的简单实现

    Netty的主页:http://netty.io/index.html 使用的Netty的版本:netty-4.0.23.Final.tar.bz2 ‐ 15-Aug-2014 (Stable, Re ...

  3. libevent源码分析:http-server例子

    http-server例子是libevent提供的一个简单web服务器,实现了对静态网页的处理功能. /* * gcc -g -o http-server http-server.c -levent ...

  4. httpserver

    改了下 # -*- coding:utf-8 -*- from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler HOST = &quo ...

  5. 一个简单的零配置命令行HTTP服务器 - http-server (nodeJs)

    http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs. 如果你不想重复的写 nodeJs 的 web-server.js, 则可以使用这个. 安装 (全局安装加 -g) ...

  6. nodejs搭建http-server

      很多时候我们都需要搭建一个简单的服务器,部署在IIS,阿帕奇,或者用nodejs,网上很多关于nodejs搭建server的文章,但都是要创建server.js,很麻烦, 在这里我分享一个创建ht ...

  7. 一、HTTPServer,RequestHandler,ServerHandler,Handler

    1.      HTTPServer,RequestHandler,ServerHandler,Handler 1.1       基本概念 HTTPServer主要是对传输控制层HTTP,TCP,S ...

  8. nodejs:本地文件夹http服务器http-server

    一.已经安装nodejs的电脑,有一个方便通过http访问本地文件夹.文件夹服务器 static files over HTTP,并不是我们平常说的node那个web服务器哦 二.好处 可以方便实现跨 ...

  9. PHP使用libevent实现高性能httpServer

    今天发现php也有一个libevent的扩展,安装后尝试下了一个webserver(httpserver), 发现性能还不错,逻辑很简单,每秒响应速度1800~4000次/s 代码如下 <?ph ...

  10. netty httpserver

    netty也可以作为一个小巧的http服务器使用. package com.ming.netty.http.httpserver; import java.net.InetSocketAddress; ...

随机推荐

  1. js判断终端是手机还是电脑

    $(function(){ function browserRedirect() { var sUserAgent= navigator.userAgent.toLowerCase(); var bI ...

  2. cocos2d-x3.6 连连看随机地图实现

    我的博客:http://blog.csdn.net/dawn_moon 这一节来讲地图初始化实现. 连连看地图初始化有非常多实现方式,大概会有下面几种: 每一格的位置随机取图片放上去 随机取图片放到随 ...

  3. Dyanmics CRM您无法登陆系统。原因可能是您的用户记录或所属的业务部门在Microoft Dynamics CRM中已被禁用

    当在操作CRM时,做不论什么的写操作包含创建数据.更新数据.都会提示以下截图中的错误:"您无法登陆系统.原因可能是您的用户记录或所属的业务部门在Microoft Dynamics CRM中已 ...

  4. SlidingMenu侧边菜单

    第一步.首先在你项目中创建一个包存放侧边菜单的类:

  5. Memcached基础

    1.实例化 MemcachedClient client = new XMemcachedClient(); public XMemcachedClient() public XMemcachedCl ...

  6. 如何实现调用console.log(‘good’.repeat(3))时输出goodgoodgood?

    String.prototype.repeat=function(num){ return (new Array(num+1)).join(this) } console.log('good'.rep ...

  7. C++学习之函数指针

     C++学习之函数指针          和数据项类似,函数也有地址,函数的地址是存储在机器语言代码的内存的开始地址.通常,这些地址对用户而言,不重要也没什么用处,但对程序而言,它却很有用. 一.函数 ...

  8. hdu 4902 Nice boat 线段树

    题目链接 给n个数, 两种操作, 第一种是将区间内的数变成x, 第二种是将区间内大于x的数变为gcd(x, a[i]). 开三个数组, 一个记录区间最大值, 这样可以判断是否更新这一区间, 一个laz ...

  9. WireShark抓包时TCP数据包出现may be caused by ip checksum offload

    最近用WireShark抓包时发现TCP数据包有报错:IP Checksum Offload,经过查阅资料终于找到了原因 总结下来就是wireshark抓到的数据包提示Checksum错误,是因为它截 ...

  10. mysql 性别存储

    大家在设计数据库时,碰到 性别.状态等 这些 值比较固定的列时,数据类型 是如何定义? 通常都是采用 : 1 create table `XXX` 2 ( 3 ........ 4 sex int(1 ...