也谈如何写一个Webserver(-)
关于如何写一个Webserver,很多大咖都发表过类似的文章.趁着这个五一假期,我也来凑个份子.
我写Webserver的原因,还得从如何将http协议传送的消息解析说起.当时,我只是想了解一下http的消息解析过程,好能够提高基于http协议的消息处理效率,所以就在网上搜了一下,发现很多人都在用nodejs的http-parser,也许是智商上限封顶^_^!,我居然没太看懂大神的代码逻辑.后来也考察过h2o这个项目的parser,无奈还是没有能领悟大神的精神^_^!.
怎么办...,挣扎了半天,最终决定硬着头皮自己写一个http消息的parser吧.就酱,就有了后来我写Maestro Webserver的故事.
既然谈到了http message的解析,那今天这第一篇随笔就谈这个东西吧.http协议的内容说起来历史太久远了,我不是历史老师,网上很多讲解都很棒,我就不多说了.此外,RFC2616, RFC7231等文档也明确的讲解了协议的含义.不过还是应该吐槽一下RFC文档的晦涩难懂哈...
还是让我引用一段相对清晰的关于http message的RFC讲解吧
HTTP messages consist of requests from client to server and responses
from server to client. HTTP-message = Request | Response ; HTTP/1.1 messages Request (section 5) and Response (section 6) messages use the generic
message format of RFC 822 [9] for transferring entities (the payload
of the message). Both types of message consist of a start-line, zero
or more header fields (also known as "headers"), an empty line (i.e.,
a line with nothing preceding the CRLF) indicating the end of the
header fields, and possibly a message-body. generic-message = start-line
*(message-header CRLF)
CRLF
[ message-body ]
start-line = Request-Line | Status-Line
从这段文字中,我们可以知道不论是request还是response,http message分三段,即start-line,message headers和message body.
那么,在设计我的messge结构体时(对了,我是用C语言开发的),我会包含这三段内容.我并没有把parser写成独立的单一函数,而是将他们分解成了一组能重复被调用的更小的函数.而从封装的角度来说,我也没有遵守尽量封装数据结构体的原则.我的目的很简单,那就是,简单易懂,容易调用(这会不会被老师调打一顿:-).
还是看看定义的数据结构体吧.
typedef struct {
int method; /* GET/POST... */
char *path;
int ver_major;
int ver_minor;
int code; /* status code */
char *status; /* status text */
sllist_t *headers;
int len_startline;
int len_headers;
unsigned char *body;
unsigned char *body_zipped;
unsigned char *body_s; /* point to the range start of the body */
size_t len_body;
} httpmsg_t;
先不用看和body相关的部分,因为我会在后续如何写Webserver中介绍相关的内容(涉及到body的压缩,断点续传等等).
下面是相关的函数,
int msg_parse(sllist_t *headers,
unsigned char **startline,
unsigned char **body,
size_t *len_body,
const unsigned char *buf);
这个是对底层message进行解析的函数,再此之上,我用两个函数封装了它,分别用于解析http request和http response.
httpmsg_t *http_parse_req(const unsigned char *buf);
httpmsg_t *http_parse_rep(const unsigned char *buf);
我写这些底层函数的原则是,尽量利用上一步的结果,不做重复的计算,比如,同一字符串的长度不要多次通过strlen计算,希望这样应该能提高(微不足道^_^!)的性能吧.
在上面的httpmsg_t结构体中,我用了单链表来管理http headers,因为headers的数量不是很多,单链表轮询反而速度更快.
至于上述函数如何实现,感兴趣朋友可以请访问我的github项目,链接https://github.com/grassroot72/Maestro2.
欢迎和我探讨..
我会在第二篇内容里介绍socket和epoll在Webserver中的应用...
也谈如何写一个Webserver(-)的更多相关文章
- 也谈如何写一个Webserver(三)
在上一篇里,我介绍了如何应用socket和epoll来组织和管理从客户端(如,浏览器)传入的连接,通过设置非阻塞连接让Webserver有更好的性能. 下面,我介绍一下在我写的Webserver Ma ...
- 比最差的API(ETW)更差的API(LTTng)是如何炼成的, 谈如何写一个好的接口
最近这几天在帮柠檬看她的APM系统要如何收集.Net运行时的各种事件, 这些事件包括线程开始, JIT执行, GC触发等等. .Net在windows上(NetFramework, CoreCLR)通 ...
- 如何写一个简单的webserver(一):最简实现
本文主要讲述如何用C/C++在Linux环境下写一个简单的支持并发的web服务器,并不考虑服务器的健壮性.安全性.性能等一系列因素. 在本文中,该服务器仅支持GET请求. 项目地址:https://g ...
- 郑晔谈 Moco 框架的开发:写一个好的内部 DSL ,写一个表达性好的程序
作者:张龙 出处:http://www.infoq.com/cn/news/2013/07/zhengye-on-moco 郑晔谈Moco框架的开发:写一个好的内部DSL,写一个表达性好的程序 作者 ...
- 张小龙谈如何写E-mail软件
编者语:鼎鼎大名的Foxmail软件制作者,你一定不会陌生吧!本刊第三期特刊还刊登过此君的生活照一张,可谓威风八面.小编此次突发奇想,“死缠烂打”,费了九牛二虎之力,终于约他写了一篇有关写E-mail ...
- Android在如何建立一个WebServer
今天老板交待任务最终完成了,感觉收获颇多,所以写一个关于它的记录,首先,看一下.老板的需求 需求: 希望移动端的用户标识(IMEI)和HTML页面的用户标识(Cookie)连接起来,当中HTML页面可 ...
- 用c++写一个 “hello,world” 的 FastCGI程序
原文:http://chriswu.me/blog/writing-hello-world-in-fcgi-with-c-plus-plus/ 上面的连接地址给出的是作者的原文地址. 另外一个作者稍微 ...
- 动手写一个简单版的谷歌TPU-指令集
系列目录 谷歌TPU概述和简化 基本单元-矩阵乘法阵列 基本单元-归一化和池化(待发布) TPU中的指令集 SimpleTPU实例: (计划中) 拓展 TPU的边界(规划中) 重新审视深度神经网络中的 ...
- 来,我们手写一个简易版的mock.js吧(模拟fetch && Ajax请求)
预期的mock的使用方式 首先我们从使用的角度出发,思考编码过程 M1. 通过配置文件配置url和response M2. 自动检测环境为开发环境时启动Mock.js M3. mock代码能直接覆盖g ...
随机推荐
- DRF 三大认证之身份认证
目录 路由组件补充 三大认证 一.身份认证 1.如何进行身份认证 2.jwt认证规则原理 3.jwt的组成 4.jwt的使用方法 4.1 签发算法 4.2 校验算法 4.3 刷新算法 二.权限认证 三 ...
- 基于docker搭建jenkins
一.概述 Jenkins 的前身是 Hudson 是一个可扩展的持续集成引擎.Jenkins 是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件.Jenkins 支持各 ...
- Selenium 4.0beta: Grid 工作原理
Selenium 4.0 beta版已经发布,那么距离正式版已经不远了,在Selenium 4.0中变化比较大的就是Grid了,本文翻译了官方文档,重点介绍Grid 4的工作原理 Selenium G ...
- 使用createrepo构建本地yum仓库
rpm包安装的时候会有很多软件会出现因为其他依赖包没有,而导致安装失败的情况.一般可以连接外网的时候我们直接使用 yum 进行安装,可以为我们解决依赖包关系,但是很多工作环境下是没有外网的,内网情况下 ...
- js--this指向的相关问题
前言 关于this的指向问题是前端面试中常考的知识点,也是我们开发学习中较难理解的问题.作为JavaScript的基础,需要我们彻底理解这一关键词.this作为JavaScript中非常复复杂的机制, ...
- 爬虫必知必会(5)_scrapy框架_基础
一.移动端数据的爬取 基于某一款抓包工具,fiddler,青花瓷,miteproxy fillder进行一个基本的配置:tools->options->connection->all ...
- Less常用变量与方法记录
需求:仅记录Lsee常用变量与方法定义,便于使用.-- @color: #000; @title-color: #000; @bg-color: #fff; @small-font: 12px; @l ...
- Python基础学习【day2】
运算符 运算符有哪些? 加 + 减 - 乘 * 除 / 幂 ** 取余 % ...
- MySQL基础知识:MySQL Connection和Session
在connection的生命里,会一直有一个user thread(以及user thread对应的THD)陪伴它. Connection和Session概念 来自Stackoverflow的一个回答 ...
- Quartz基础使用
Quartz基本组成部分: 调度器:Scheduler 任务:JobDetail 触发器:Trigger,包括SimpleTrigger和CronTrigger . using Quartz; usi ...