以前产品应用是用串口做控制台,写了一个带简单命令历史和命令补全功能的控制台Shell,用作程序的调试,包括查看系统状态和调试修改设定等等。确实非常好用,对很多现场简单问题的快速定位起到了很好的作用。系统移到 Linux 以后,由于对如何在 Linux 下,在应用程序中如何嵌入控制台 Shell 用作原先的调试功能,不太熟悉,先前想用 Modbus Server,通过改 Modbus 寄存器方式做调试修改,后来发现根本要用的时候记不住,也不直观,不好用,写完了以后,自己就没用过。后来想集成一个 httpd,使用 CGI 接口做程序状态的相识和调试修改设定,于是着手寻找简单的 httpd 的实现。经过一番比较,我认为 bhttpd 非常小,c 文件只有3个,行数只有 104+375+90 行。而且带 CGI 功能,十分适合拿来用。

下来代码以后测试发现,这个代码有问题,不能正常工作。沉下心来,把代码看了一遍,做了一番调试,代码终于正常了。修改后的代码更新在我的 bhttpd 上。其中大概修改如下:

  1. 字符行分成多个字符串

这个功能基本就是 C# 的 split 方法,原实现类似于:

char buf[BUFFER_SIZE];
char basic_request[3][BUFFER_SIZE];
// other codes
read_line(buf, sockfd);
sscanf(buf, "%s %s %s", basic_request[METHOD], basic_request[PATH], basic_request[PROC]);

这个方法很简单,就是比较浪费内存。当然似乎在系统上,这点内存可能不算啥。但是对于嵌入的人来说,不能忍受,做了一个原地切割为多个字符串的函数来处理:

int str_split(char *str, char split_char, char **ret, int max_ret)
{
int ret_num = 0, i;
memset(ret, 0, max_ret*sizeof(*ret));
while (ret_num < max_ret) {
*ret++ = str;
ret_num++;
while ((*str != split_char) && (*str != '\0')) str++;
if (*str == '\0') break;
*str = '\0'; str++;
while (*str == split_char) str++;
if (*str == '\0') break;
} for (i = ret_num; i < max_ret; i++) // all other pointers set to NULL string
*ret++ = str;
return ret_num;
}

由此,独立出来一个单独的 strlibs 模块,专门放置字符串处理相关的工具函数。

  1. 头文件包含:原先的实现,头文件主要包含在 .h 文件中。我始终认为,.h 中应该包含最少的头文件,如果实现需要某个头文件,那么包含应该是在 .c 中。所以大量的包含做了修改。

  2. mime 3级加速表,改动态分配为全静态分配。因为不管如何,他的元素始终是这么多的,那就不如静态分配,对调试更有利,也更易理解。

  3. tcp 连接 close() 调用问题

其实,这个问题才是这个库不能工作的最终原因。其解释可以见 The ultimate SO_LINGER page, or: why is my tcp not reliable 代码修改为:

if(fds[i].fd!=-1&&(fds[i].revents & POLLRDNORM)) {
handle_request(mime_tbl, cgi_tbl, conf.pub_dir, conf.default_page, fds[i].fd);
close(fds[i].fd);
fds[i].fd = -1;
--polled;
}

修改为:

if (fds[i].fd != -1 && (fds[i].revents & POLLRDNORM)) {
handle_request(mime_tbl, cgi_tbl, conf.pub_dir, conf.default_page, fds[i].fd);
shutdown(fds[i].fd, SHUT_WR);
while ((len = recv(fds[i].fd, buff, sizeof(buff), 0)) > 0) fprintf(stderr, "recv before close get %d.\n", len);
close(fds[i].fd);
fds[i].fd = -1;
--polled;
}

注意 shutdown() 函数以后后面的 recv() 函数循环。

bhttpd的更多相关文章

  1. [http服务]

    [http服务] CentOS 6 httpd 程序环境 记录了httpd的主进程编号: v 主程序文件: /usr/sbin/httpd /usr/sbin/httpd.worker /usr/sb ...

  2. http服务配置和apache

    CentOS 6 httpd 程序环境 记录了httpd的主进程编号:    主程序文件: /usr/sbin/httpd /usr/sbin/httpd.worker /usr/sbin/http ...

随机推荐

  1. c#+cad2010+MQ接收消息

    cad2015+版本可以使用TrayItem气泡显示消息 static TrayItem trayItem = new TrayItem(); public static void testtrayi ...

  2. The usage of docker image wurstmeister/kafka

    The docker image wurstmeister/kafka is the most stared image for kafka in hub.docker.com, but the us ...

  3. 页面中关于bootstrap框架的增删改查使用

    bootstrap是一个简单又好用的前端框架 1.bootstrap 初始化  表格显示 2.自带的查询表单(需要配置要查询的条件  对应实体类) 3.工具(增加和查询) 4.查询方法 5.增加方法 ...

  4. 关于如何使用ehcarts2加载svg矢量地图并自定义县级内部乡镇轮廓

    项目需求:显示县级内部的乡镇一级地图的轮廓! 效果预览: 阻碍因素:echarts不提供县级以下乡镇级轮廓. 解决思路: 1.根据资料查找相关县的行政区域图(百度搜索),如本人所制作的浙江省宁波市宁海 ...

  5. 【HDFS API编程】查看HDFS文件内容、创建文件并写入内容、更改文件名

    首先,重点重复重复再重复: /** * 使用Java API操作HDFS文件系统 * 关键点: * 1)创建 Configuration * 2)获取 FileSystem * 3)...剩下的就是 ...

  6. ubuntu安装后环境配置

    首先实现能够连接外网,宿主机和虚拟机能ping通 进行smb的配置,能实现pytty的远程连接. 首先apt-get update 安装ssh apt-get install ssh 打开ssh服务 ...

  7. Oracle修改系统参数导致系统无法启动对pfile文件恢复。

    1.当前现象:Oracle启动报错ORA-27102 [oracle@JYDB1 ~]$ sqlplus / as sysdba SQL*Plus: Release 10.2.0.4.0 - Prod ...

  8. JdbcTemplate 多数据源 jdbc

    参考: https://www.cnblogs.com/tangzekai/p/7782773.html 配置: package top.zekk.twodatasource.config; impo ...

  9. Linux命令:内建命令

    本文对内建命令进行归类,便于学习和记忆. 分类 内建命令 同义词 功能相反命令 定义&声明类 alias   unalias declare typeset   local     reado ...

  10. [Kafka] |FAIL|rdkafka#producer-1 : Receive failed: Disconnected

    Why am I seeing Receive failed: Disconnected? 1. broker 的空闲连接回收器关闭不活跃连接. 由 broker 的配置属性 connections. ...