图解丨在嵌入式设备上实现HTTP服务器
ID:嵌入式基础学习
作者:小二
本期为大家带来一个 WiFi 应用的实际场景,其实在之前「我对 WiFi 驱动移植过程,做了一次总结复盘」这篇文章中有简单提过,但由于内容较多,就单独摘出来了。
来自读者的催更😭😭😭,别着急,小二在努力了!
1 应用场景
我喜欢讲一个东西,先来探讨下他的应用场景。
毕竟知道了用在哪,怎么用,才能写好代码嘛,哈哈😉
本次项目,实际的应用场景:通过手机 APP ,连接到设备 WiFi 热点,进行设备的参数配置;
接着结合实际框架,拆分需求如下:
采用 HTTP 协议,使用 POST 方法; 设备端需要作为一个 HTTP 服务器; 不需要支持 CGI ,通过 APP 展示界面即可;
好了,明白了需求,接下来就是实现方案了。
2 方案论证
刚开始的时候,一点头绪都没有。
组长指导的一种方案,就是在新唐数据手册中看到的,采用 Lighttpd 的方案。
后来去查资料,发现一篇介绍不同 HTTP 服务器的文章,挺不错的,如下
常见的几种嵌入式web服务器(https://www.cnblogs.com/quliuliu2013/p/12786301.html)
2.1 Lighttpd
这块刚开始是同事在搞,后来我这边的需求规划出来后,就在想能否使用这种方案。
毕竟方案是现成的,可以节省不少时间。
后来经过分析,发现 Lighttpd 需要一个单独的进程执行,如下图所示
Lighttpd 提供了 CGI 接口,支持 IE 访问固定界面,然后进行参数配置。
实际实现方案,我认为比较好的方法是,Lighttpd 进程接收到参数变更时,写入配置文件 A ,主业务进程监测文件 A 是否有改变,如果检测到改变,则读一次数据。
具体实现方式,参考下图
结合实际情况分析,
1、目前只维护一个主业务进程,如果再增加额外的进程,则维护成本将大大增加。
2、实际不需要 CGI 接口,不需要支持 IE ,APP 做界面显示即可。
经过论证,此方案较复杂,暂且当做最后的备选方案。
2.2 cpp-httplib
接着我就去 GitHub 上寻找 HTTP Server ,发现 cpp-httplib
这个比较好用的库。
GitHub 链接:cpp-httplib(https://github.com/yhirose/cpp-httplib)
在查看了 ReadMe 文件后,很遗憾,我用不了😑😑😑
提示说 GCC 4.8 及以下版本无法正常编译,因为 <regex>
文件已损坏。。。
我去找了找解决方法,发现在 GCC 4.9 版本修复了这个问题,参考 Stack Overflow 回答如下
https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions
没办法了,我们当前 GCC 版本是 4.8.3 的,肯定不能因为这个库就更换编译器呀,那只能再去找找看咯。
2.3 httpserver
然后就接着去搜索,发现了 httpserver
这个库,只有一个 .h
头文件,感觉很简单。
GitHub 链接:httpserver(https://github.com/jeremycw/httpserver.h)
分析本质需求,发现只需要在主进程中,跑一个 HTTP Server 的线程,监听固定端口,然后采用 HTTP 协议进行通信即可。
简要功能,如下图所示
从上图可以看出
1、主进程中,单独跑一个 HTTP Server 的线程,监听固定端口 8888 ;
2、此线程同时进行数据处理,将参数写入文件 A ;
3、其他业务线程,在需要参数时,直接去文件 A 获取最新参数即可;
3 实现方式
具体实现方式,参考 ReadMe 文件,也很方便实现。
简述一下主要流程:
绑定监听端口号,绑定回调函数;
死循环监听端口;
当需要关闭 HTTP 服务时,通过 flag 标志位,改变当前状态;
关闭 HTTP 服务后,需要释放相应资源;
所有的配置处理接口,在 HandleRequest 回调函数中;
(PS:我这电脑网络有问题,GitHub 一直打不开,手机热点也不行,暂时还没别的好办法,只能麻烦您自己去网页上看啦。)
4 注意事项
在使用过程中,我这遇到一个问题。
您看下边这块代码,是这样的
void hs_init_session(http_request_t* session) {
session->flags = HTTP_AUTOMATIC;
session->parser = (http_parser_t){};
session->stream = (hs_stream_t){};
if (session->tokens.buf) {
free(session->tokens.buf);
session->tokens.buf = NULL;
}
http_token_dyn_init(&session->tokens, 32);
}
在第 3 、4 行末尾,直接就是一个大括号,里边什么都没写。
然后我的程序在这个地方就一直编译不过去。后来在公司前辈指点下,按照下图所示,添加了 0 之后,就能编译通过了。
推测原因是当前编译器使用的 C 标准,不支持这么高级的用法。
注意:需要修改的不止这一处,其他地方如有编译报错,也需做类似修改。
5 总结
针对本次的功能需求,最困难的地方,在于寻找一个合适的 HTTP Serve 库来使用。
过程虽然艰难,但也锻炼了自己找东西的能力。
好了各位看官,本次的分享到此结束,如果您还意犹未尽,大可从头再看。
最后别忘了赏小二一个 赞 和 在看 哦😚😚😚
嵌入式编程专辑 Linux 学习专辑 C/C++编程专辑 Qt进阶学习专辑
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享。