大部分的web server都是用JAVA、JS做的,查找了许多资料github上也只有寥寥数篇的几篇帖子,能用的真不多。后来经同事帮助在《嵌入式网络那些事STM32物联实战》这本书上找到了基于协议栈LWIP的web server,建立服务器需要对HTTP协议、html网页有所了解,这样更利于设计代码完善功能(公司要求功能简单点灯、OTA升级、PWM波形输出、mesh网络配置)。
github示例:https://github.com/volkanunal/LwipFreertosWebServer
废话不多说直接上代码
一、tcp接口实现
#define HTTP_PORT 80
const unsigned char htmldata[] = " \
<html> \
<head><title> A LwIP WebServer !!</title></head> \
<center><p>A WebServer Based on LwIP v1.4.1!</center>\
</html>";
static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
char *data = NULL;
/* We perform here any necessary processing on the pbuf */
if (p != NULL)
{
/* We call this function to tell the LwIp that we have processed the data */
/* This lets the stack advertise a larger window, so more data can be received*/
tcp_recved(pcb, p->tot_len);
data = p->payload;
if(p->len >=3 && (strncmp(data, "GET /", 5) == 0)
{
tcp_write(pcb, htmldata, sizeof(htmldata), 1);
}
else
{
printf("Request error\n");
}
pbuf_free(p);
tcp_close(pcb); //断开连接,有可能导致打开不了网页
}
else if (err == ERR_OK)
{
/* When the pbuf is NULL and the err is ERR_OK, the remote end is closing the connection. */
/* We free the allocated memory and we close the connection */
return tcp_close(pcb);
}
return ERR_OK;
}
/**
* @brief This function when the Telnet connection is established
* @param arg user supplied argument
* @param pcb the tcp_pcb which accepted the connection
* @param err error value
* @retval ERR_OK
*/
static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
tcp_recv(pcb, http_recv);
return ERR_OK;
}
/**
* @brief Initialize the http application
* @param None
* @retval None
*/
static void http_server_init(void)
{
struct tcp_pcb *pcb = NULL;
/* Create a new TCP control block */
pcb = tcp_new();
/* Assign to the new pcb a local IP address and a port number */
/* Using IP_ADDR_ANY allow the pcb to be used by any local interface */
tcp_bind(pcb, IP_ADDR_ANY, HTTP_PORT);
/* Set the connection to the LISTEN state */
pcb = tcp_listen(pcb);
/* Specify the function to be called when a connection is established */
tcp_accept(pcb, http_accept);
}
注意:1.一般会先发送消息报头head
例如:const static char http_html_hdr[] = "HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n";
但是现在直接发送网页实体也能正常显示网页,不同浏览器兼容性问题也会有影响
二、lwip的API接口实现网页控灯
const static char http_html_hdr[] = "HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n";
const unsigned char LedOn_Data[] = "\
<!DOCTYPE HTML>\
<HTML>\
<head>\
<meta charset=\"UTF-8\">\
<title>LED Monitor</title></head>\
<body>\
<center>\
<p>\
<center>LED 已打开!</center>\
<form method=\"post\" action=\"off\" name=\"ledform\">\
<font size=\"2\">改变LED状态:</font> \
<input type=\"submit\" value=\"关闭\">\
</form>\
</p>\
</center>\
</body>\
</HTML>";
const unsigned char LedOff_Data[] = "\
<!DOCTYPE HTML>\
<HTML>\
<head>\
<meta charset=\"UTF-8\">\
<title>LED Monitor</title></head>\
<body>\
<center>\
<p>\
<center>LED 已关闭!</center>\
<form method=\"post\" action=\"on\" name=\"ledform\">\
<font size=\"2\">改变LED状态:</font> \
<input type=\"submit\" value=\"打开\">\
</form>\
</p>\
</center>\
</body>\
</HTML>";
const unsigned char testData[] = "\
<HTML>1</HTML>";
static bool led_on = FALSE;
void httpserver_send_html(struct netconn *conn, bool led_status)
{
err_t err;
//err = netconn_write(conn, http_html_hdr, sizeof(http_html_hdr)-1, NETCONN_NOCOPY);
//不发送head的原因是发送了头,网页显示不了,空白,但可以和实体的数组合并一起发送,这样会更稳定
printf("err = %d\n", err);
if(led_status == TRUE) {
netconn_write(conn, LedOn_Data, sizeof(LedOn_Data)-1, NETCONN_NOCOPY);
} else {
err = netconn_write(conn, LedOff_Data, sizeof(LedOff_Data)-1, NETCONN_NOCOPY);
printf("err = %d\n", err);
printf("%s,%d\n",__func__,__LINE__);
}
printf("%s,%d\n",__func__,__LINE__);
}
void httpserver_serve(struct netconn *conn)
{
struct netbuf *inbuf;
err_t recv_err;
char *buf;
u16_t buflen;
recv_err = netconn_recv(conn, &inbuf);
printf("%s,%d\n",__func__,__LINE__);
if(recv_err == ERR_OK) {
if(netconn_err(conn) == ERR_OK) {
netbuf_data(inbuf, (void**)&buf, &buflen);
printf("%s,%d\n",__func__,__LINE__);
if((buflen >= 5) && (strncmp(buf, "GET /", 5) == 0)) {
httpserver_send_html(conn, led_on);
printf("%s,%d\n",__func__,__LINE__);
} else if((buflen >= 8) && (strncmp(buf, "POST", 4) == 0)) {
printf("%s,%d\n",__func__,__LINE__);
if(buf[6] == '0' && buf[7] == 'n') {
led_on = TRUE;
LED_ON(); //自己根据情况在这设置
} else if(buf[6] == '0' && buf[7] == 'f' && buf[8] == 'f') {
led_on = FALSE;
LED_OFF();
}
httpserver_send_html(conn, led_on);
}
}
netbuf_delete(inbuf);
}
netconn_close(conn);
}
static void
httpserver_thread(void *arg)
{
struct netconn *conn, *newconn;
err_t err;
LWIP_UNUSED_ARG(arg);
/* Create a new TCP connection handle */
conn = netconn_new(NETCONN_TCP);
LWIP_ERROR("http_server: invalid conn", (conn != NULL), return;);
led_on = TRUE;
LED_ON(); //根据实际情况编写函数
/* Bind to port 80 (HTTP) with default IP address */
netconn_bind(conn, NULL, 80);
/* Put the connection into LISTEN state */
netconn_listen(conn);
do {
err = netconn_accept(conn, &newconn);
if (err == ERR_OK) {
httpserver_serve(newconn);
netconn_delete(newconn);
}
} while(err == ERR_OK);
LWIP_DEBUGF(HTTPD_DEBUG,
("http_server_netconn_thread: netconn_accept received error %d, shutting down",
err));
netconn_close(conn);
netconn_delete(conn);
}
/** Initialize the HTTP server (start its thread) */ //根据自身情况启用线程
void
httpserver_init()
{
sys_thread_new("http_server_netconn", httpserver_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPIP_THREAD_PRIO + 1);
}
开启web server之前需要先保持开发板与服务器端处于同一网段,并能ping通,推荐使用google浏览器进行调试
在公司进行开发时还是要多进行debug和实验调试,祝大家早日完成任务