也谈如何写一个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 ...
随机推荐
- JUnit5学习之五:标签(Tag)和自定义注解
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- Aliyun Oss 上传文件
目录 Aliyun OSS OSS 简介 OSS 基本概念 OSS 功能概述 OSS 使用 创建存储空间Bucket 创建子目录 Java编码 测试 Aliyun OSS OSS 简介 阿里云对象存储 ...
- rest framework renderers
渲染器 前TemplateResponse实例可以被返回给客户端,它必须被渲染.渲染过程需要模板和上下文的中间表示,并把它变成能够提供给客户端的最后一个字节流. - Django文档 REST框架包含 ...
- rest framework Genericview
通用视图 Django的通用视图...被开发为普通使用模式的快捷方式......他们采取某些共同的习惯和模式的发展观和抽象,从而使您可以快速地将数据写入的共同看法,而不必重复自己发现的. - Djan ...
- c++ 11 是如何简化你的数据库访问接口的
之前写过一篇文章专门分析了 c++ 模板编译过程中报的一个错误:<fatal error C1045: 编译器限制 : 链接规范嵌套太深 >,其中涉及到了 qtl -- 一个使用 c++ ...
- Python爬虫学习一------HTTP的基本原理
昨天刚买的崔大大的<Python3网络爬虫开发实战>,今天就到了,开心的读完了爬虫基础这一章,现记录下自己的浅薄理解,如有见解不到位之处,望指出. 1.HTTP的基本原理 ①我们经常会在浏 ...
- 题解 洛谷P1990 覆盖墙壁
DP康复训练题 原题:洛谷P1990 核心:递推/DP 题源应该是铺地砖,所以采用一摸一样的思路,只是有两种不同的方块 我们先用最最简单的方式尝试一下枚举当最后一行被填满的情况: 1.如果我们只用第一 ...
- Sentry-JS-SDK-Browser 官方示例最佳实践
系列 SDK 开发 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(理念与设计原则篇) 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(开发基础篇) 顶级开源项目 Sent ...
- Object类中的常用方法
1.getClass方法 源码: 功能: 返回此Object的运行时类. 什么是运行时类? 如上图所示,类从被加载到虚拟机内存开始,到卸载出内存为止,他的生命周期一共包含7个阶段.其中加载阶段虚拟机需 ...
- DAOS 分布式异步对象存储|架构设计
分布式异步对象存储 (DAOS) 是一个开源的对象存储系统,专为大规模分布式非易失性内存 (NVM, Non-Volatile Memory) 设计,利用了SCM(Storage-Class Memo ...