C语言编写web server

申屠鹏
2023-12-01

大部分的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和实验调试,祝大家早日完成任务

 

 类似资料: