自己写http服务器

为了从性能和易用性上测试我写的mu_event的效果,想写一个网络应用来验证一下,正好我的下一个目标是http服务器,所以我决定动手写一个小型的http服务器。

其实开源的http服务器有很多,但是出名的都是比较重量级的,不利于学习。所以我找了几个小型的http server项目,抽出其中的http协议解析部分,改动部分代码后,应用在我的项目中使用。

我本以为很快就能完成一个基本的项目,后来却折腾了很多时间,而且只实现了一个基本的http静态文件服务器,post请求还不支持。一方面是对http协议还没有到非常熟悉的地步,导致协议解析的核心部分经常在改动。另一方面是对http的某些机制也不清晰,实现对应的功能时就费了点功夫。还有就是内存的bug也折腾了两天,毕竟用C语言实现要自己管理内存的,而且我也不想用智能指针来托管内存,因为我不想让用来学习的项目过于复杂。

完成了基本的HTTP GET请求,先告一段落。实现后续的post请求,甚至CGI需费点功夫,这个以后有时间再添加吧。这里记录一下自己完成这个项目过程中遇到的坑,以及一些技巧,还有心得体会。

http协议有短连接和长连接的概念,短连接就是指客户端(浏览器)每次请求都会建立一个连接,即每次连接都要完成三次握手,释放时要四次挥手。对应的,长连接就是指多次请求复用同一个连接,以达到提高效率的目的。对于短连接,服务器要主动断开连接,否则浏览器会一直等待(这是经过测试得知的,那么为什么浏览器不能主动关闭呢?)。而对于长连接尽量不要主动断开连接,为什么呢?这涉及到tcp连接的状态,主动断开的连接会有一个TIME_WAIT的状态,这个状态会持续2MSL的时间(各个系统根据参数实现的不同,通常为2min),这会影响高并发时的性能。因此最好在客户端断开连接。

那客户端什么时候断开呢?由服务器去通知行的通吗?可以这样,超过一定时间后,下次回复客户端时设置Connection: close标识,这样客户端就会主动断开了。如果服务器没有下次回复的机会,那么只有等待客户端主动断开连接了。客户端超时是会主动关闭的,在360浏览器上测试是110s(这个时间不是从第一次连接开始计时的,而是空闲连接时间)。如果客户端没有超时断开机制,那只能在服务器做超时处理,然后主动断开了。

另外,http协议响应部首中有个Keep-Alivet字段,他有个timeout参数,据说可以通知客户端超时断开连接。但是我测试了360和chrome浏览器之后,发现根本没有用,《http权威指南》给出的答案是,这个字段并不是个承诺值,也就是说客户端不一定会实现这个。

在实现项目的过程中遇到的问题:

长连接延迟

用ab进行长连接测试的时候,发现要比短链接要慢很多。这就奇怪了,按道理长连接的效率肯定要高于短链接的,因为没有短链接每次都要握手的开销。我以为是解析http请求时出了问题了,然后对解析的部分单独进行时间统计,发现在长连接和短链接下的时间都差不多。

排除了这个因素,我又以为是在长连接状态下发送文件出了问题,毕竟第一次用的sendfile,心里没谱啊。然后把发送文件改成改成发送固定的字符串,发现还是有这个问题。最后脑子里突然灵光一现,是不是tcp delay延时的问题。把tcp选项加上no delay果然好了,在网上搜索类似的文章,发现早有人遇到过这个问题,大名鼎鼎的nginx还专门有这个配置选项。

下面的文章就是对这个问题的探讨:

https://www.cnblogs.com/wajika/p/6573014.html

https://blog.csdn.net/sunxiaopengsun/article/details/73130695

总结:在write-write-read,且长连接的情况下,tcp的nagle算法会生效。我在实现http响应的过程中,正好是先write header line,然后再write body,引起tcp nagle算法延时40ms。设置tcp选项,禁用nagle就没有这个问题了。

访问内存的出错问题

第一次遇到内存访问出错的问题是在获取请求缓冲区数据的时候,当时怎么调试都无法解决,反复确认没有发现有内存相关的错误。后来在解决警告时发现,是调用那个函数没有找到声明,故返回值是个默认的int,但真正该函数返回的是char*,在64位系统上占8字节,导致返回值高4位被随机填充。这次也给了我一个很好的教训,一定要重视警告,最好用-Wall显示所有的警告。

再次遇到内存的问题也很诡异,显示在malloc上报错。我也反复检查过内存分配和释放的地方,没有发现问题。后来定位到是访问已释放掉的内存造成的,这也我对一个http请求和回复整个逻辑流程混乱后果。后来优化了逻辑流程,对每个请求和回复都对应严格的生命周期阶段。

通过这次经历也体会到了用C语言写项目的一个痛点--内存管理,当出现由访问内存造成的错误时是很难定位的,经常好几个小时甚至几天都束手无策。C语言内存问题常常被称为C语言程序猿的梦魇,也是有道理的。除了有基本的‘分配和释放要配对,不能使用已经释放调用的内存’等常识是远远不够的,不规范的编程习惯,对逻辑流程的不清不楚也会诱发内存相关的问题。

项目地址:
https://github.com/shonm520/mwebserver

欢迎star。

欢迎加入QQ群 858791125 讨论skynet,游戏后台开发,lua脚本语言等问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值