RT-Thread 软件包-软件包分类-IoT-Web***①
Web***
中文页 | English
1、介绍
Web*** 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的 Web 服务器实现,它不仅提供设备与 HTTP Client 通讯的基本功能,而且支持多种模块功能扩展,且资源占用少、可裁剪性强,充分满足开发者对嵌入式设备服务器的功能需求。
Web*** 软件包功能特点如下:
- 支持 HTTP 1.0/1.1
- 支持 AUTH 基本认证功能
- 支持 CGI 功能
- 支持 ASP 变量替换功能
- 支持 SSI 文件嵌入功能
- 支持 INDEX 目录文件显示功能
- 支持 ALIAS 别名访问功能
- 支持文件上传功能
- 支持预压缩功能
- 支持缓存功能
- 支持断点续传功能
更多软件包功能特点介绍请查看 详细介绍。
1.1 目录结构
名称 | 说明 |
---|---|
docs | 文档目录 |
inc | 头文件目录 |
src | 源文件目录 |
module | 功能模块文件目录 |
samples | 示例文件目录 |
LICENSE | 许可证文件 |
README.md | 软件包使用说明 |
SConscript | RT-Thread 默认的构建脚本 |
1.2 许可证
Web*** 软件包遵循 GPL2+ 商业双许可。该软件包可以根据 GNU 标准使用通用公共许可证,详见 LICENSE 文件。如果用于商业应用,可以通过电子邮箱 <business@rt-thread.*** > 与我们联系获取商业许可。
1.3 依赖
- RT_Thread 3.0+
- DFS 文件系统
2、 获取软件包
使用 Web***软件包需要在 RT-Thread 的包管理中选中它,具体路径如下:
RT-Thread online packages
IoT - inter*** of things --->
[*] Web***: A HTTP Server for RT-Thread
(80) Server listen port
(16) Maximum number of server connections
(/web***) Server root directory
Select supported modules --->
[ ] LOG: Enanle output log support
[ ] AUTH: Enanle basic HTTP authentication support
[ ] CGI: Enanle ***mon Gateway Interface support
[ ] ASP: Enanle Active Server Pages support
[ ] SSI: Enanle Server Side Includes support
[ ] INDEX: Enanle list all the file in the directory support
[ ] ALIAS: Enanle alias support
[ ] DAV: Enanle Web-based Distributed Authoring and Versioning support
[ ] UPLOAD: Enanle upload file support
[ ] GZIP: Enable ***pressed file support by GZIP
(0) CACHE: Configure cache level
[ ] Enable web*** samples
Version (latest) --->
Server listen port:配置服务器监听端口号;
Maximum number of server connections:配置服务器最大连接数量;
Server root directory:配置服务器根目录路径;
Select supported modules:选择服务器支持的功能模块;
Enable web*** samples :配置添加服务器示例文件;
Version:配置软件包版本。
配置完成后让 RT-Thread 的包管理器自动更新,或者使用 pkgs --update 命令更新包到 BSP 中。
3、使用 Web*** 软件包
- 软件包详细介绍,请参考 软件包介绍
- 详细的示例介绍,请参考 示例文档
- 如何从零开始使用,请参考 用户指南
- 完整的 API 文档,请参考 API 手册
- 软件包工作原理,请参考 工作原理
- 更多详细介绍文档位于
/docs
文件夹下,使用软件包进行开发前请务必查看。
4、注意事项
- Web*** 软件包使用需要文件系统支持,需要确保运行设备上能使用文件系统。
- Web*** 软件包默认未开启任何模块功能支持,使用的需要根据软件包介绍在 Env 中开启需要的功能。
示例程序
Web*** 软件包提供了一个综合的示例页面用于展示软件包的多项功能,包括:AUTH、CGI、ASP、SSI、INDEX、ALIAS、Upload 等功能。本章节主要介绍 Web*** 软件包中各个功能模块示例的使用方式。
示例文件
示例程序路径 | 说明 |
---|---|
samples/wn_sample.c | 综合示例代码 |
samples/wn_sample_upload.c | 上传文件示例代码 |
samples/index.html | 综合示例页面 |
samples/index.shtml | SSI 功能示例页面 |
samples/version.asp | ASP 功能示例页面 |
准备工作
软件包获取
- menuconfig 配置获取软件包和示例代码
打开 RT-Thread 提供的 Env 工具,使用 menuconfig 配置软件包。启用 Web*** 软件包,并配置使能测试例程配置(Enable web*** samples),如下所示:
RT-Thread online packages
IoT - inter*** of things --->
[*] Web***: A HTTP Server for RT-Thread
(80) Server listen port ## 服务器监听套接字端口号
(16) Maximum number of server connections ## 服务器最大支持的连接数
(/web***) Server root directory ## 服务器根目录
Select supported modules ---> ## 默认开启使用的功能模块
[ ] LOG: Enanle output log support
-*- AUTH: Enanle basic HTTP authentication support
-*- CGI: Enanle ***mon Gateway Interface support
-*- ASP: Enanle Active Server Pages support
-*- SSI: Enanle Server Side Includes support
-*- INDEX: Enanle list all the file in the directory support
-*- ALIAS: Enanle alias support
[ ] DAV: Enanle Web-based Distributed Authoring and Versioning support
-*- UPLOAD: Enanle upload file support
[ ] GZIP: Enable ***pressed file support by GZIP
(0) CACHE: Configure cache level
[*] Enable web*** samples ## 开启测试例程
Version (latest) --->
-
使用
pkgs --update
命令下载软件包 -
编译下载
页面文件准备
Web*** 软件包示例中需要获取本地静态页面,需要文件系统的支持(FAT 文件系统,ROMFS 文件系统等,只需要支持 RT-Thread 的设备虚拟文件系统)。
静态页面需要上传到文件系统中服务器根目录下(示例中使用根目录为 /web***)。设备挂载文件系统成功,需要依次执行下面操作:
-
使用
mkdir web***
命令创建 Web*** 软件包根目录 /web***,并使用cd web***
命令进入该目录; -
使用
mkdir admin
和mkdir upload
命令创建 /web***/admin 和 /web***/upload ,用于 AUTH 功能和 Upload 功能测试; -
将 Web*** 软件包 /sample 目录下的:index.html、index.shtml、version.asp 三个文件依次上传到设备 /web*** 目录(Web*** 根目录)中。(可以使用 TFTP 工具上传文件,具体操作方式参考 TFTP 使用说明)
创建目录和上传文件成功之后,就可以启动例程,测试 Web*** 软件功能。
启动例程
本例程参数和环境配置如下:
-
监听端口号:80
-
根目录地址:/web***
-
文件系统:FAT 文件系统
设备启动,连接网络成功之后,在 Shell 命令行输入 web***_test
命令启动 Web*** 服务器。查看 Shell 命令行,显示如下日志信息,说明 Web*** 服务器初始化成功:
msh />web***_test
[I/wn] RT-Thread web*** package (V2.0.0) initialize su***ess.
然后在 Shell 命令行中使用 ifconfig
命令获取本设备 IP地址为 192.168.12.29。
msh />ifconfig
***work interface: w0 (Default)
MTU: 1500
MAC: 44 32 c4 75 e0 59
FLAGS: UP LINK_UP ETHARP BROADCAST IGMP
ip address: 192.168.12.29
gw address: 192.168.10.1
*** mask : 255.255.0.0
dns server #0: 192.168.10.1
dns server #1: 223.5.5.5
接着在浏览器(这里使用谷歌浏览器)中输入设备 IP 地址,将默认访问设备根目录下 /index.html 文件,如下图所示,页面文件正常显示:
该页面上显示了 Web*** 软件包的基本功能介绍,并根据不同的功能给出相应的演示示例,下面将按顺序介绍如下几个例程:
- AUTH 基本认证例程
- CGI 事件处理例程
- ASP 变量替换例程
- SSI 文件嵌套例程
- INDEX 目录显示例程
- ALIAS 别名访问例程
- Upload 文件上传例程
AUTH 基本认证例程
在例程主页( /index.html)AUTH Test 模块下点击 基本认证功能测试:用户名及密码为 admin:admin
按键,弹出基本认证对话框,输入用户名 admin,密码 admin。成功输入用户名和密码后,会进入根目录下 /admin 目录,如下图所示流程:
CGI 事件处理例程
本例程提供两个 CGI 示例:hello world 例程 和 calc 例程 ,用于演示 CGI 事件处理的基本功能。
- hello world 例程
hello world 例程演示了在页面演示文本内容功能,在例程主页 CGI Test 模块下点击 > hello world
按键,会跳转到新页面显示日志信息,新页面显示了 hello world 说明 CGI 事件处理成功,然后在新页面点击 Go back to root
按键回到例程主页面,如下图所示:
- calc 例程
calc 例程中使用 CGI 功能在页面上展示了简单的加法计算器功能,在例程主页 CGI Test 模块下点击 > calc
按键,会跳转到新的页面,输入两个数字点击 计算
按键,页面会显示两数字相加的结果。在新页面点击 Go back to root
按键可以回到例程主页面,如下图所示:
ASP 变量替换例程
ASP 例程演示了页面变量替换的功能。在例程主页 ASP Test 模块下点击 ASP 功能测试:访问 version.asp 文件
按键,会跳转到根目录下 version.asp 页面,该页面显示当前使用 RT-Thread 系统的版本号。在新页面点击 Go back to root
按键回到例程主页面,如下图所示:
SSI 文件嵌套例程
SSI 例程演示在一个页面中嵌套另一个页面的功能。在例程主页 SSI Test 模块下点击 SSI 功能测试:访问 /version.shtml 页面
按键,会跳转到根目录下 index.shtml 页面,该页面中嵌套显示了主页面 index.html 内容。在新页面点击 Go back to root
按键回到例程主页面。
INDEX 目录显示例程
INDEX 例程演示页面文件列表功能。
首先需要任意上传一个文件到根目录的 /admin 目录中,例程中已上传了 admin.txt 文件到该目录中。
然后在例程主页 INDEX Test 模块下点击 INDEX 功能测试:访问/admin 目录
按键,会跳转到根目录下 /admin 目录,并且列出该目录下所有文件名和文件长度,如下图所示:
ALIAS 别名访问例程
ALIAS 例程演示了使用目录别名访问该目录的功能。该例程代码中已经将 /test 目录设置别名为 /admin, 在例程主页 ALIAS Test 模块下点击 ALIAS 功能测试:访问 /test 目录会跳转到 /admin 目录
按键,实际我们访问的是 /test 目录,但会跳转访问 /admin 目录,并列出该目录下所有文件信息,如下图所示:
Upload 文件上传例程
Upload 例程实现上传文件到 Web*** 服务器固定目录功能。在例程主页上 Upload File Test 模块下点击 选择文件
按键,选取需要上传的文件(本例程是用 upload.txt 文件),点击 上传
,可以将文件上传到根目录下的 /upload 目录,如下图所示:
文件上传成功之后,返回例程主页,点击 浏览上传文件的目录
按键,可以访问 /upload 文件,查看刚才上传的文件信息。
示例代码
…\src\web***.c
/*
* File : web***.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively for ***mercial application, you can contact us
* by email <business@rt-thread.***> for ***mercial license.
*
* Change Logs:
* Date Author Notes
* 2011-08-02 Bernard the first version
* 2012-07-03 Bernard Add web*** port and webroot setting interface.
*/
#include <stdint.h>
#include <string.h>
#include <sys/errno.h>
#include <web***.h>
#include <wn_module.h>
#ifdef SAL_USING_POSIX
#include <sys/select.h>
#else
#include <lwip/select.h>
#endif
#if defined(RT_USING_LWIP) && (RT_LWIP_TCPTHREAD_STACKSIZE < 1408)
#error The lwIP tcpip thread stack size(RT_LWIP_TCPTHREAD_STACKSIZE) must more than 1408
#endif
#define DBG_ENABLE
#define DBG_COLOR
#define DBG_SECTION_NAME "wn"
#ifdef WEB***_USING_LOG
#define DBG_LEVEL DBG_LOG
#else
#define DBG_LEVEL DBG_INFO
#endif /* WEB***_USING_LOG */
#include <rtdbg.h>
static rt_uint16_t web***_port = WEB***_PORT;
static char web***_root[64] = WEB***_ROOT;
static rt_bool_t init_ok = RT_FALSE;
void web***_set_port(int port)
{
RT_ASSERT(init_ok == RT_FALSE);
web***_port = port;
}
int web***_get_port(void)
{
return web***_port;
}
void web***_set_root(const char* webroot_path)
{
rt_strncpy(web***_root, webroot_path, sizeof(web***_root) - 1);
web***_root[sizeof(web***_root) - 1] = '\0';
}
const char* web***_get_root(void)
{
return web***_root;
}
/**
* web*** thread entry
*/
static void web***_thread(void *parameter)
{
int listenfd = -1;
fd_set readset, tempfds;
fd_set writeset, tempwrtfds;
int sock_fd, maxfdp1;
struct sockaddr_in web***_saddr;
struct timeval rcv_to = {0, 50000};
/* First acquire our socket for listening for connections */
listenfd = socket(AF_I***, SOCK_STREAM, IPPROTO_TCP);
if (listenfd < 0)
{
LOG_E("Create socket failed.");
goto __exit;
}
rt_memset(&web***_saddr, 0, sizeof(web***_saddr));
web***_saddr.sin_family = AF_I***;
web***_saddr.sin_addr.s_addr = htonl(INADDR_ANY);
web***_saddr.sin_port = htons(web***_port); /* web*** server port */
/* Set receive timeout for a***ept() */
if(setsockopt(listenfd, SOL_SOCKET, SO_RCVTIMEO, (void*)&rcv_to, sizeof(rcv_to)) == -1)
{
LOG_E("Set SO_RCVTIMEO failed, errno=%d\n", errno);
goto __exit;
}
if (bind(listenfd, (struct sockaddr *) &web***_saddr, sizeof(web***_saddr)) == -1)
{
LOG_E("Bind socket failed, errno=%d\n", errno);
goto __exit;
}
/* Put socket into listening mode */
if (listen(listenfd, WEB***_CONN_MAX) == -1)
{
LOG_E("Socket listen(%d) failed.", WEB***_CONN_MAX);
goto __exit;
}
/* initialize module (no session at present) */
web***_module_handle_event(RT_NULL, WEB***_EVENT_INIT);
/* Wait forever for ***work input: This could be connections or data */
for (;;)
{
/* Determine what sockets need to be in readset */
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(listenfd, &readset);
/* set fds in each sessions */
maxfdp1 = web***_sessions_set_fds(&readset, &writeset);
if (maxfdp1 < listenfd + 1)
{
maxfdp1 = listenfd + 1;
}
/* use temporary fd set in select */
tempfds = readset;
tempwrtfds = writeset;
/* Wait for data or a new connection */
sock_fd = select(maxfdp1, &tempfds, &tempwrtfds, 0, 0);
if (sock_fd == 0)
{
continue;
}
/* At least one descriptor is ready */
if (FD_ISSET(listenfd, &tempfds))
{
struct web***_session* a***ept_session;
/* We have a new connection request */
a***ept_session = web***_session_create(listenfd);
if (a***ept_session == RT_NULL)
{
/* create session failed, just a***ept and then close */
int sock;
struct sockaddr cliaddr;
socklen_t clilen;
clilen = sizeof(struct sockaddr_in);
sock = a***ept(listenfd, &cliaddr, &clilen);
if (sock >= 0)
{
closesocket(sock);
}
}
else
{
/* add read fdset */
FD_SET(a***ept_session->socket, &readset);
}
}
web***_sessions_handle_fds(&tempfds, &writeset);
}
__exit:
if (listenfd >= 0)
{
closesocket(listenfd);
}
}
int web***_init(void)
{
rt_thread_t tid;
if (init_ok == RT_TRUE)
{
LOG_I("RT-Thread web*** package is already initialized.");
return 0;
}
tid = rt_thread_create(WEB***_THREAD_NAME,
web***_thread, RT_NULL,
WEB***_THREAD_STACKSIZE, WEB***_PRIORITY, 5);
if (tid != RT_NULL)
{
rt_thread_startup(tid);
init_ok = RT_TRUE;
LOG_I("RT-Thread web*** package (V%s) initialize su***ess.", WEB***_VERSION);
}
else
{
LOG_E("RT-Thread web*** package (V%s) initialize failed.", WEB***_VERSION);
return -1;
}
return 0;
}
维护人:
- 华为奋斗者精神, 邮箱:1992152446@qq.***