如何使用HTTP服务器搭建本地Web网站

描述

最近在学习 ETH 模块,ETH 模块具有 HTTP 服务的功能,我将在本帖中探讨如何使用 HTTP 服务搭建一个本地 Web网站,并分享一些我所获得的经验。

1. HTTP介绍:

HTTP是超文本传输协议(HypertextTransfer Protocol)的缩写,它是用于传输超文本(如 HTML)数据的应用层协议。HTTP是万维网上数据通信的基础,它被用于在Web浏览器和Web服务器之间传输信息。

HTTP是一个无状态协议,这意味着每个单独的请求都是独立的,服务器不会在多个请求之间保留任何数据。每个HTTP请求从客户端(例如Web浏览器)发送到服务器,然后服务器返回一个响应。这种请求-响应模型使得客户端可以从服务器获取各种信息,如网页、图像、视频等。

HTTP通常使用TCP作为其传输层协议,通过使用标准端口号80进行通信(HTTPS使用端口号443)。近年来,随着对安全性的需求增加,基于HTTP的加密版本HTTPS也变得越来越普遍,它通过使用SSL/TLS协议来加密数据传输,确保数据的机密性和完整性。

HTTP服务特点:

HTTP服务具有以下几个主要特点:

1. 无连接:HTTP是一种无连接的协议,即每个请求和响应之间都是独立的,服务器不会保留关于客户端的任何状态信息。这种无连接的特性使得服务器能够更有效地处理大量的并发请求。

2. 无状态:HTTP是一种无状态的协议,即服务器不会在请求之间保留任何状态信息。每个请求都是独立的,服务器不会记住之前的请求信息。这种设计简化了服务器的管理和维护,但也意味着服务器无法跟踪客户端的状态,需要使用其他机制来实现状态管理,如使用Cookies或Session。

3. 简单灵活:HTTP的设计简单且灵活,易于实现和使用。它使用文本格式的请求和响应消息,易于调试和理解。同时,HTTP也支持多种不同的请求方法(如GET、POST、PUT、DELETE等),以及多种不同的内容类型(如文本、图像、音频、视频等),使得其适用于各种不同的应用场景。

4. 基于请求-响应模型:HTTP是基于请求-响应模型的协议,即客户端发送一个请求给服务器,服务器处理请求并返回一个响应给客户端。这种模型使得客户端能够从服务器获取各种信息,如网页、图像、视频等。

5. 支持多媒体内容:HTTP不仅可以传输文本数据(如HTML),还可以传输图像、视频、音频等多媒体内容。这使得互联网上的各种资源可以通过HTTP服务进行传输和访问。

6. 基于TCP协议:HTTP通常使用TCP作为其传输层协议,通过使用标准端口号80进行通信(HTTPS使用端口号443)。TCP协议提供了可靠的数据传输机制,确保数据的可靠性和完整性。

综上所述,HTTP服务具有无连接、无状态、简单灵活等特点,适用于各种不同的应用场景,是互联网上数据通信的基础。

LWIP1.4.1的HTTP服务介绍

lwIP(lightweight IP)是一个轻量级的开源TCP/IP协议栈,用于嵌入式系统和小型设备。lwIP1.4.1版本中包含了一个简单的HTTP服务器,可以用于在嵌入式设备上搭建基本的Web服务器。

lwIP1.4.1版本的HTTP服务器具有以下特点:

1.轻量级:lwIP是一个轻量级的TCP/IP协议栈,适用于资源受限的嵌入式系统和小型设备。其HTTP服务器也是精简设计,适合在资源有限的环境下运行。

2.基于C语言:lwIP的HTTP服务器是用C语言编写的,易于移植和集成到各种嵌入式系统中。

3.支持基本功能:lwIP的HTTP服务器支持基本的HTTP功能,如处理GET请求、发送静态内容(如HTML页面、图像等)、处理简单的动态内容等。

4.定制化:虽然lwIP的HTTP服务器功能相对简单,但可以根据需要进行定制和扩展,以满足特定应用场景的需求。

5.适用性:lwIP的HTTP服务器适用于嵌入式设备上需要提供简单Web服务的场景,如远程监控、配置管理、固件升级等。

总的来说,lwIP1.4.1版本的HTTP服务器是一个简单而实用的工具,适合在资源受限的嵌入式系统中搭建基本的Web服务器功能。

2. 本地 Web 服务器搭建的步骤

1.新建工程,移植lwip库,并包含其所需要的组件。

Web

2.准备一些html文件,用于界面显示和控制。

Web

3. 使用makefsdata将html 文件转化为c语言数组形式。(附件含 makefsdata工具)

首先打开makefsdata目录,新建fs文件夹。

Web

把准备好的文件复制到fs文件夹中。

Web

退回上一级目录,即makefsdata根目录下,打开cmd,输入“makefsdata“,即可把fs文件夹中的html文件转化为fsdata.c文件,该文件包含了其转化后的c语言数组。

Web

生成的 fsdata.c 文件,就是我们需要的源文件。

Web

这样我们的基础文件数据就准备好了。

4. 编写httpd_cgi_ssi.c文件

HTTP的SSI和CGI介绍:

当涉及到 Web 服务器上的动态内容处理时,两种常见的方法是Server Side Includes (SSI) 和 Common GatewayInterface(CGI)。

### 1. Server Side Includes (SSI):

SSI 是一种简单的动态内容生成技术,它允许在 HTML 页面中嵌入动态内容。SSI 在 HTML 文件中通过特殊的标签实现,服务器在响应客户端请求时动态地处理这些标签。

#### 如何使用SSI:

- SSI 标签通常以 结束。

- 常见的SSI指令包括:

- #include:包含其他文件的内容。

- #echo:输出环境变量或者其他值。

- #exec:执行外部命令并将结果输出。

- SSI 通常在服务器配置中启用,并且需要指定哪些文件扩展名应该被解析为SSI。

#### 优点:

- 简单易用,无需编写额外的代码。

- 可以直接在 HTML 文件中嵌入动态内容,方便快捷。

#### 缺点:

- 功能有限,主要用于简单的动态内容生成。

- 对服务器性能有一定影响,因为需要在每个请求中动态解析处理SSI标签。

### 2. Common Gateway Interface (CGI):

CGI 是一种更为灵活和强大的动态内容生成技术。它允许服务器调用外部程序来处理客户端请求,并生成动态内容。CGI程序可以用任何编程语言编写,只要能够通过标准输入和输出与 Web 服务器通信即可。

#### 如何使用CGI:

- CGI 程序通常位于 Web 服务器的特定目录中(如`cgi-bin` 目录)。

- 当服务器收到客户端请求时,会调用相应的 CGI 程序来处理请求,并将结果返回给客户端。

- CGI 程序通过环境变量获取客户端请求信息,并通过标准输出返回动态生成的内容。

#### 优点:

- 灵活多样,可以使用各种编程语言编写CGI 程序。

- 可以处理复杂的动态内容生成需求,如表单处理、数据库查询等。

#### 缺点:

- 比较复杂,需要编写额外的程序。

- 对服务器性能影响较大,每个 CGI请求都需要启动一个新的进程来处理。

综上所述,SSI 适用于简单的动态内容生成需求,而CGI 则更适合处理复杂的动态内容生成任务。选择哪种方法取决于具体的需求和服务器环境。

在了解这些之后,我们看看下面编写的httpd_cgi_ssi.c文件源码。

#include"lwip/debug.h"

#include"httpd.h"

#include"lwip/tcp.h"

#include"fs.h"

#include"main.h"

#include"Board.h"

#include

#include

tSSIHandlerADC_Page_SSI_Handler;

uint32_tADC_not_configured=1;

/* wewill use character "t" as tag for CGI */

charconst* TAGCHAR="t";

charconst** TAGS=&TAGCHAR;

/*CGI handler for LED control */

constchar * LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char*pcValue[]);

/*Html request for "/leds.cgi" will start LEDS_CGI_Handler */

consttCGI LEDS_CGI={"/leds.cgi", LEDS_CGI_Handler};

/*Cgi call table, only one CGI used */

tCGICGI_TAB[1];

/**

*[url=home.php?mod=space&uid=247401]@brief[/url]  Configures the ADC.

* @param None

* @retval None

*/

staticvoid ADC_Configuration(void)

{

ADC_Config_TadcConfig;

ADC_CommonConfig_TadcCommonConfig;

GPIO_Config_TgpioConfig;

RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC3);

RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOF);

gpioConfig.pin= GPIO_PIN_9;

gpioConfig.mode= GPIO_MODE_AN;

gpioConfig.pupd= GPIO_PUPD_NOPULL;

GPIO_Config(GPIOF,&gpioConfig);

ADC_CommonConfigStructInit(&adcCommonConfig);

adcCommonConfig.mode= ADC_MODE_INDEPENDENT;

adcCommonConfig.prescaler= ADC_PRESCALER_DIV6;

adcCommonConfig.accessMode= ADC_ACCESS_MODE_DISABLED;

adcCommonConfig.twoSampling= ADC_TWO_SAMPLING_5CYCLES;

ADC_CommonConfig(&adcCommonConfig);

ADC_ConfigStructInit(&adcConfig);

adcConfig.resolution= ADC_RESOLUTION_12BIT;

adcConfig.scanConvMode= DISABLE;

adcConfig.continuousConvMode= ENABLE;

adcConfig.extTrigEdge= ADC_EXT_TRIG_EDGE_NONE;

adcConfig.dataAlign= ADC_DATA_ALIGN_RIGHT;

adcConfig.nbrOfChannel= 1;

ADC_Config(ADC3,&adcConfig);

ADC_ConfigRegularChannel(ADC3,ADC_CHANNEL_7,1,ADC_SAMPLETIME_56CYCLES);

ADC_Enable(ADC3);

/*ADC3 regular Software Start Conv */

ADC_SoftwareStartConv(ADC3);

}

/**

* [url=home.php?mod=space&uid=247401]@brief[/url]  ADC_Handler : SSI handler for ADC page

*/

u16_tADC_Handler(int iIndex, char *pcInsert, int iInsertLen)

{

/* We have only one SSI handler iIndex = 0 */

if (iIndex ==0)

charDigit1=0, Digit2=0, Digit3=0, Digit4=0;

uint32_tADCVal = 0;

/* configure ADC if not yet configured */

if (ADC_not_configured ==1)  

{

ADC_Configuration();

ADC_not_configured=0;

}

/* get ADC conversion value */

ADCVal = ADC_ReadConversionValue(ADC3);

/* convert to Voltage,  step = 0.8 mV */

ADCVal = (uint32_t)(ADCVal * 0.8); 

printf("ADC Value: %d ",ADCVal);

/* get digits to display */

Digit1= ADCVal/1000;

Digit2= (ADCVal-(Digit1*1000))/100 ;

Digit3=(ADCVal-((Digit1*1000)+(Digit2*100)))/10;

Digit4= ADCVal -((Digit1*1000)+(Digit2*100)+(Digit3*10));

/* prepare data to be inserted in html */

*pcInsert  = (char)(Digit1+0x30);

*(pcInsert + 1) = (char)(Digit2+0x30);

*(pcInsert + 2) = (char)(Digit3+0x30);

*(pcInsert + 3) = (char)(Digit4+0x30);

/* 4characters need to be inserted in html*/

return4;

}

return 0;

}

/**

*[url=home.php?mod=space&uid=247401]@brief[/url]  CGI handler for LEDs control

*/

constchar * LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char*pcValue[])

{

uint32_t i=0;

/* We have only one SSI handler iIndex = 0 */

if (iIndex==0)

{

/*All leds off */

APM_TINY_LEDOff(LED2);

APM_TINY_LEDOff(LED3);

/*Check cgi parameter : example GET /leds.cgi?led=2&led=4 */

for(i=0; i

{

/* check parameter "led" */

if (strcmp(pcParam[i] ,"led")==0)  

{

/*switch led2 ON if 2 */

if(strcmp(pcValue[i],"2") ==0)

APM_TINY_LEDOn(LED2);

/*switch led3 ON if 3 */

elseif(strcmp(pcValue[i], "3") ==0)

APM_TINY_LEDOn(LED3);

}

}

}

/* uri to send after cgi call*/

return "/APM32F407LED.html"; 

}

/**

* Initialize SSI handlers

*/

voidhttpd_ssi_init(void)

{

/* configure SSI handlers (ADC page SSI) */

http_set_ssi_handler(ADC_Handler, (char const**)TAGS, 1);

}

/**

* Initialize CGI handlers

*/

voidhttpd_cgi_init(void)

{

/* configure CGI handlers (LEDs control CGI)*/

CGI_TAB[0] = LEDS_CGI;

http_set_cgi_handlers(CGI_TAB, 1);

}

这段代码是一个基于lwIP(LightweightIP)的HTTP服务器的实现,它允许通过网页控制单片机上的LED,并且能够实时获取ADC(模数转换器)的值并显示在网页上。让我们逐个分析每个函数及其功能:

- ADC_Configuration():

 - 这个函数配置了微控制器的ADC模块,使其准备好进行模拟信号的数字化转换。

 - 配置了ADC3通道7,并启动了ADC转换。

-ADC_Handler():

 - 这个函数是SSI(Server Side Include)的处理函数,用于处理ADC页面的SSI标签。

 - 它读取ADC转换的值,将其转换为电压值,并将其插入到HTML页面的指定位置。

 - 该函数返回要插入的字符数量。

-LEDS_CGI_Handler():

 - 这个函数是CGI(Common Gateway Interface)的处理函数,用于处理LED控制的CGI请求。

 - 它检查CGI参数,根据参数设置LED的状态(开或关)。

 - 返回一个字符串,指示CGI调用完成后应该跳转的页面。

- httpd_ssi_init():

 - 这个函数初始化SSI处理程序,将ADC页面的SSI处理函数注册到HTTP服务器。

-httpd_cgi_init():

 - 这个函数初始化CGI处理程序,将LED控制的CGI处理函数注册到HTTP服务器。

总体来说,这段代码实现了一个基本的HTTP服务器,可以通过网页界面控制LED,并实时显示ADC转换的值。通过SSI和CGI,可以动态地生成网页内容,并实现与单片机硬件的交互。

5.编写main函数

intmain(void)

{

charLCDDisplayBuf[100] = {0};

structip_addr DestIPaddr;

uint8_tflag = 0;

USART_Config_TusartConfig;

/*User config the different system Clock */

UserRCMClockConfig();

/*Configure SysTick */

ConfigSysTick();

/*Configure USART */

usartConfig.baudRate= 115200;

usartConfig.wordLength= USART_WORD_LEN_8B;

usartConfig.stopBits= USART_STOP_BIT_1;

usartConfig.parity= USART_PARITY_NONE ;

usartConfig.mode= USART_MODE_TX_RX;

usartConfig.hardwareFlow= USART_HARDWARE_FLOW_NONE;

APM_BOARD_COMInit(COM1,&usartConfig);

/*Configures LED2 and LED3 */

APM_BOARD_LEDInit(LED2);

APM_BOARD_LEDInit(LED3);

/*KEY init*/

APM_BOARD_PBInit(BUTTON_KEY1,BUTTON_MODE_GPIO);

APM_BOARD_PBInit(BUTTON_KEY2,BUTTON_MODE_GPIO);

printf("Thisis a Demo! ");

/*Configure ethernet (GPIOs, clocks, MAC, DMA) */

ConfigEthernet();

/*Initilaize the LwIP stack */

LwIP_Init();

httpd_init();

/*Use Com printf static IP address*/

sprintf(LCDDisplayBuf,"TINYboard Static IP address ");

printf("%s",LCDDisplayBuf);

sprintf(LCDDisplayBuf,"IP:%d.%d.%d.%d ",

IP_ADDR0,

IP_ADDR1,

IP_ADDR2,

IP_ADDR3);

printf("%s",LCDDisplayBuf);

sprintf(LCDDisplayBuf,"NETMASK:%d.%d.%d.%d ",

NETMASK_ADDR0,

NETMASK_ADDR1,

NETMASK_ADDR2,

NETMASK_ADDR3);

printf("%s",LCDDisplayBuf);

sprintf(LCDDisplayBuf,"Gateway:%d.%d.%d.%d ",

GW_ADDR0,

GW_ADDR1,

GW_ADDR2,

GW_ADDR3);

printf("%s",LCDDisplayBuf);

while(1)

{

/*check if any packet received */

if(ETH_CheckReceivedFrame())

{

/*process received ethernet packet */

LwIP_Pkt_Handle();

}

/*handle periodic timers for LwIP */

LwIP_Periodic_Handle(ETHTimer);

}

}

6.配置开发板静态IP地址

Web

3. 实验现象:

1.用以太网接口线,连接开发板与PC端,打开浏览器,输入开发板的IP地址,进入网页端。

Web

2.在网页端点击Led control,跳转到led控制界面。

Web

Web

3.点击ADC StatusBar,跳转到ADC采集界面。

Web

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分