• 做一个幸福的人,读书,旅行,努力工作,关心身体和心情。
  • 不管有没有人爱,也要努力做一个可爱的人。不埋怨谁,不嘲笑谁,也不羡慕谁,阳光下灿烂,风雨中奔跑,做自己的梦,走自己的路。

基于 libmicrohttpd 的 HTTP 服务器初探

Network lcq 10个月前 (10-30) 421次浏览 0个评论

最近写一个小项目,需要用到HTTP Server来支持一个API操作,正如同现在如火如荼的云API泛滥时代,感觉你不弄个开放接口都不好意思说自己是互联网公司。要求是用POST方式以及JSON数据格式发一个请求,然后服务端返回一个JSON格式的结果。当然用HTTP格式而非Socket方式的好处是显而易见的,HTTP是建立在socket之上的一个应用层协议,虽然性能可能不是最好的,但意味着你不用定义数据包的协议格式、容错以及通信过程中的各种繁琐的细节,只需要关注用户发送的有效负载信息及自己的处理逻辑设计实现就可以了。

说到HTTP可能最先想到的是Apache和Nginx,然而寡人不是做前端的,也不懂全世界最好的语言(PHP),更不晓得RESTful API,HTTP收到的JSON数据还不晓得怎么和后台的CGI结合起来。于是,这次另辟蹊径找到了libmicrohttpd,话说这个libmicrohttpd是用C写的,GNU旗下产品,支持Linux、Windows、以及andriod、symbian等平台(其定位就是作为一个嵌入式的HTTP库,方便集成到各个程序中去),支持SSL、session等机制,不过每个部分都比较简单,算是简单实现了一个HTTP的框架吧。

一、HTTP协议 – libmicrohttpd

libmicrohttpd的文档算是比较的详细,但是用起来还是有点需要注意的:

1.1 处理POST JSON数据格式的请求

libmicrohttpd本身提供了POST数据的处理接口,在新连接到时候使用MHD_create_post_processor创建POST处理器,收到数据后,调用MHD_post_process处理,只不过得到的数据表示为带=的键值对形式,这对我们这种JSON数据显然是不行的。

如果需要自己收取处理数据,然后自己用JSON库来解析处理,那么我们需要做的是,将完整的数据收上来,然后自己手动处理数据。其思路为:

(1)在create_response函数中,调用MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);,这会解析HTTP头部得到数据的长度,依据这个长度创建接受缓冲区;

(2)不断地收数据,直到接受不到数据为止,然后检查收到的数据长度和之前头部报告的长度是否相等;

(3)调用自己的数据处理接口。

代码的样式如下:

1.2 服务端运行模式

在启动libmicrohttpd服务MHD_start_daemon的时候有很多选项,其中最重要的是指定服务器的线程工作模式,大体来说分为以下几类:

MHD_USE_THREAD_PER_CONNECTION

MHD会启动一个线程来进行侦听,当得到一个连接的时候,会生成一个新的线程来处理这个连接,由于连接之间没有通信机制,所以适用于无状态的连接,但是如果创建线程的开销比较大的时候,显然效率不高;

MHD_USE_SELECT_INTERNALLY

创建一个线程来处理侦听和处理,这种情况要被认为响应是不会阻塞,而且能够快速返回的,不然这个线程处于忙等待,系统的吞吐量会被严重影响。

MHD_USE_POLL

包括派生的EPOLL等形式,是I/O+线程池的最佳服务器模式,当使用这些模式的时候,需要使用MHD_OPTION_THREAD_POOL_SIZE来设置使用线程池的大小。然后你可以在处理函数中,打印当前threadid来验证线程池是否工作正常。我个人用的是终极MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY模式,然后MHD_OPTION_THREAD_POOL_SIZE线程池设置为6个线程。

1.3 返回文件系统的资源文件

这个部分已经在哥的业务需求之外了。在前面的create_response之中,等于做了个钩子——对于请求的url是/api的时候,就接受数据并进行JSON解析,其实对于其他URL访问,可以返回文件系统中对应文件给客户,就是普通的WebServer模式,于是添加了send_page_from_file(connection, url);这么个函数来处理其他URL请求

其实上面的函数核心就是MHD_create_response_from_fd,当然你也可以把整个文件读出来,然后调用MHD_create_response_from_data来发送,官方的手册上说data函数对于小的可以放到内存上的数据或者文件(比如静态页面)比较合适,后者适用于不知道发送数据的具体大小或者数据过大不能放到内存的情况,不过觉得上面这样用也挺方便的。(不知道是不是要保存这个handle,然后在request_completed中关闭,如果后续发现描述符泄露再改吧。) 然后顺便模拟了DocumentRoot、Indexes等特性,配置过apache的应当很熟悉。

 

二、参考

1、json-c

2、GNU Libmicrohttpd

3、如何直接获取 libmicrohttpd 库中POST上来的整个数据

4、MySQL C API

5、MySQLdb Documentation

6、Writing C API Threaded Client Programs


乐趣公园 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明基于 libmicrohttpd 的 HTTP 服务器初探
喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址