找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2193|回复: 1
打印 上一主题 下一主题
收起左侧

一个简单的嵌入式平台httpd服务器源程序

[复制链接]
跳转到指定楼层
楼主
分享一个简单地http服务器源代码,适合于无系统的嵌入式平台。


源程序如下:
  1. /*
  2. * httpd.c
  3. *
  4. *  Created on: 2019年09月01日
  5. *      Author: YAO Zhanhong
  6. */


  7. #include <stdint.h>
  8. #include <stdbool.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <udelay.h>

  13. #include "SPI_W5500/SPI_W5500.h"
  14. #include "html.h"
  15. #include "httpd.h"
  16. #include "httpd_htm.h"


  17. SOCKET      l_http_socket = W5500_SOCKET_INVALID;

  18. uint8_t     l_request_buffer[HTTP_REQUEST_BUFF_SIZE];     /* Request的缓冲 */
  19. uint16_t    l_request_length;                             /* Request的长度 */

  20. char*       l_request_param_list[HTTP_REQUEST_PARAM_MAX]; /* Request的参数列表,指向l_request_buffer中的地址 */
  21. uint16_t    l_request_param_num;                          /* 解析后,Request的参数个数 */

  22. char*       l_request_method;                             /* 解析后,Request的方法 */
  23. char*       l_request_URL;                                /* 解析后,Request的URL */
  24. char*       l_request_version;                            /* 解析后,Request的版本 */
  25. char*       l_request_URI;                                /* 解析后,Request的URI */

  26. char*       l_request_content;                            /* 解析后,Request的正文 */
  27. uint16_t    l_request_content_len;                        /* 实际接收到的正文长度 */
  28. uint16_t    l_request_content_len_desired;                /* 请求头中指示的Request的正文长度,即正文应收长度 */

  29. uint16_t    l_cur_process_pos;                            /* 指在Request缓冲中,当前处理到的位置 */


  30. /*****************************************************************************************************************************
  31. * 回调函数,由驱动循环调用,传递接收到的socket数据
  32. * 在本函数中,将socket传递的数据追加到http服务器的处理缓冲中,具体的协议处理则在http服务器的loop中进行
  33. * 因为一次接收可能无法将Request的全部请求数据传递完成,所以需要先缓冲起来,凑成足够大的request请求数据,在loop中集中处理
  34. * 即使这样,由于缓冲有限,所以当POST数据超长时,服务器仍会无法处理
  35. *****************************************************************************************************************************/
  36. void on_http_request(SOCKET s_client, uint8_t* data, uint16_t data_len)
  37. {
  38.         if ( l_http_socket != s_client )
  39.         {
  40.                 return;
  41.         }

  42.         if (data != NULL && data_len > 0 )
  43.         {
  44.                 for ( int i = 0; i < data_len; i++ )
  45.                 {
  46.                         printf("%c", data[i]);
  47.                 }
  48.                 printf("\n");

  49.                 /* 把接收到的数据添加到request缓冲 */
  50.                 if ( l_request_length + data_len <= HTTP_REQUEST_BUFF_SIZE )
  51.                 {
  52.                         memcpy( l_request_buffer + l_request_length, data, data_len );
  53.                         l_request_length += data_len;
  54.                 }
  55.                 else
  56.                 {
  57.                         memcpy( l_request_buffer + l_request_length, data, HTTP_REQUEST_BUFF_SIZE - l_request_length );
  58.                         l_request_length = HTTP_REQUEST_BUFF_SIZE;
  59.                 }
  60.         }
  61. }


  62. /*****************************************************************************************************************************
  63. * 重置全部request接收变量
  64. *****************************************************************************************************************************/
  65. void reset_request()
  66. {
  67.         memset(l_request_buffer, 0, HTTP_REQUEST_BUFF_SIZE);
  68.         l_request_length = 0;

  69.         for (uint16_t i = 0; i < HTTP_REQUEST_PARAM_MAX; i++ )
  70.         {
  71.                 l_request_param_list[i] = NULL;
  72.         }
  73.         l_request_param_num = 0;

  74.         l_request_method = NULL;
  75.         l_request_URL = NULL;
  76.         l_request_version = NULL;
  77.         l_request_URI = NULL;

  78.         l_request_content = NULL;
  79.         l_request_content_len = 0;
  80.         l_request_content_len_desired = 0;

  81.         l_cur_process_pos = 0;
  82. }

  83. /*****************************************************************************************************************************
  84. * 获取完整的一行
  85. * 返回值:该行的字符总数,包括结尾的回车换行,为0表示未找到结尾的回车换行
  86. *****************************************************************************************************************************/
  87. uint16_t get_line(uint8_t* data, uint16_t data_len)
  88. {
  89.         uint16_t ret_len = 0;

  90.         if ( data != NULL && data_len >= 2 )
  91.         {
  92.                 for ( uint16_t i = 0; i < data_len - 1; i++ )
  93.                 {
  94.                         if ( data[i] == 0x0D && data[i+1] == 0x0A )
  95.                         {
  96.                                 ret_len = i + 2;
  97.                                 break;
  98.                         }
  99.                 }
  100.         }

  101.         return ret_len;
  102. }


  103. /*****************************************************************************************************************************
  104. * 解析Request的第一行,即:方法/URL/版本
  105. *****************************************************************************************************************************/
  106. void parse_request_first(uint8_t* data, uint16_t data_len, char** method, char** URL, char** version)
  107. {
  108.         uint16_t  cur_pos = 0;

  109.         *method = NULL;
  110.         *URL = NULL;
  111.         *version = NULL;

  112.         if ( data != NULL && data_len >= 2 )
  113.         {
  114.                 *method = (char *)data;
  115.                 for ( uint16_t i = cur_pos; i < data_len; i++ )
  116.                 {
  117.                         if ( data[i] == 0x20 )
  118.                         {
  119.                                 data[i] = 0;
  120.                                 cur_pos = i + 1;
  121.                                 break;
  122.                         }
  123.                 }

  124.                 *URL = (char *)(data + cur_pos);
  125.                 for ( uint16_t i = cur_pos; i < data_len; i++ )
  126.                 {
  127.                         if ( data[i] == 0x20 )
  128.                         {
  129.                                 data[i] = 0;
  130.                                 cur_pos = i + 1;
  131.                                 break;
  132.                         }
  133.                 }

  134.                 *version = (char *)(data + cur_pos);
  135.                 for ( uint16_t i = cur_pos; i < data_len; i++ )
  136.                 {
  137.                         if ( data[i] == 0x0D )
  138.                         {
  139.                                 data[i] = 0;
  140.                                 break;
  141.                         }
  142.                 }
  143.         }
  144. }


  145. /*****************************************************************************************************************************
  146. * 解析Request头信息
  147. *****************************************************************************************************************************/
  148. void parse_request_header(uint8_t* data, uint16_t data_len, char** item, char** value)
  149. {
  150.         uint16_t  cur_pos = 0;

  151.         *item = NULL;
  152.         *value = NULL;

  153.         if ( data != NULL && data_len >= 2 )
  154.         {
  155.                 *item = (char *)data;
  156.                 for ( uint16_t i = cur_pos; i < data_len; i++ )
  157.                 {
  158.                         if ( data[i] == 0x3A ) /* 寻找":" */
  159.                         {
  160.                                 data[i] = 0;
  161.                                 cur_pos = i + 1;
  162.                                 break;
  163.                         }
  164.                 }

  165.                 *value = (char *)(data + cur_pos);
  166.                 for ( uint16_t i = cur_pos; i < data_len; i++ )
  167.                 {
  168.                         if ( data[i] == 0x0D )
  169.                         {
  170.                                 data[i] = 0;
  171.                                 break;
  172.                         }
  173.                 }
  174.         }
  175. }


  176. /*****************************************************************************************************************************
  177. * 解析Request的传递参数
  178. *****************************************************************************************************************************/
  179. uint16_t parse_request_parameter(uint8_t* data, uint16_t data_len, char** param_list, uint16_t param_max)
  180. {
  181.         uint16_t  i;
  182.         uint16_t  param_num = 0;
  183.         uint16_t  param_pos = 0;

  184.         for ( uint16_t i = 0; i < param_max; i++ )
  185.         {
  186.                 param_list[i] = NULL;
  187.         }

  188.         if ( data != NULL && data_len >= 0 )
  189.         {
  190.                 while ( param_pos < data_len )
  191.                 {
  192.                         if ( param_num >= param_max )
  193.                         {
  194.                                 break;
  195.                         }

  196.                         param_list[param_num] = (char *)(data + param_pos);

  197.                         for ( i = param_pos; i < data_len; i++ )
  198.                         {
  199.                                 if ( data[i] == 0x26 ) /* 判断是否有&符号 */
  200.                                 {
  201.                                         data[i] = 0;
  202.                                         param_pos = i + 1;
  203.                                         param_num++;
  204.                                         break;
  205.                                 }
  206.                         }

  207.                         if ( i == data_len )
  208.                         {
  209.                                 param_num++;
  210.                                 break;
  211.                         }
  212.                 }
  213.         }

  214.         return param_num;
  215. }


  216. /*****************************************************************************************************************************
  217. * 解析Request的URL
  218. *****************************************************************************************************************************/
  219. uint16_t parse_request_URL(uint8_t* data, char** URI, char** param_list, uint16_t param_max)
  220. {
  221.         uint16_t  i;
  222.         uint16_t  url_len = 0;
  223.         uint16_t  param_num = 0;
  224.         uint16_t  param_pos = 0;

  225.         for ( uint16_t i = 0; i < param_max; i++ )
  226.         {
  227.                 param_list[i] = NULL;
  228.         }

  229.         if ( data != NULL )
  230.         {
  231.                 url_len = (uint16_t)strlen(data);
  232.         }

  233.         if ( url_len > 0 )
  234.         {
  235.                 *URI = (char *)data;
  236.                 if( url_len > 1 )
  237.                 {
  238.                         param_pos = url_len; /* 默认没有参数 */

  239.                         for ( i = 0; i < url_len; i++ )
  240.                         {
  241.                                 if ( data[i] == 0x3F ) /* 通过查找问号,来判断是否有参数 */
  242.                                 {
  243.                                         data[i] = 0;
  244.                                         param_pos = i + 1;
  245.                                         break;
  246.                                 }
  247.                         }

  248.                         if ( param_pos < url_len )
  249.                         {
  250.                                 param_num = parse_request_parameter( data + param_pos, url_len - param_pos, param_list, param_max );
  251.                         }
  252.                 }
  253.         }

  254.         return param_num;
  255. }

  256. /**********************************************************************
  257. * 服务器的出错响应
  258. **********************************************************************/
  259. void http_respond_error(SOCKET s_client, uint16_t err_code)
  260. {
  261.         char      buf[128];
  262.         uint8_t*  html_data = NULL;
  263.     uint16_t  html_file_len = 0;
  264.         uint16_t  html_send_pos = 0;
  265.         uint16_t  cur_send_len = 0;

  266.         switch (err_code)
  267.         {
  268.         case 400:
  269.             sprintf(buf, "HTTP/1.0 400 Bad Request\r\n");
  270.             html_data = g_Respond_400;
  271.             html_file_len = sizeof(g_Respond_400);
  272.                 break;
  273.         case 404:
  274.             sprintf(buf, "HTTP/1.0 404 Not Found\r\n");
  275.             html_data = g_Respond_404;
  276.             html_file_len = sizeof(g_Respond_404);
  277.                 break;
  278.         case 501:
  279.             sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  280.             html_data = g_Respond_501;
  281.             html_file_len = sizeof(g_Respond_501);
  282.                 break;
  283.         case 505:
  284.             sprintf(buf, "HTTP/1.0 505 Nonsupport http Version\r\n");
  285.             html_data = g_Respond_505;
  286.             html_file_len = sizeof(g_Respond_505);
  287.                 break;
  288.         default:
  289.             sprintf(buf, "HTTP/1.0 %d ERROR\r\n", err_code);
  290.         }
  291.     W5500_send(s_client, buf, strlen(buf));

  292.     sprintf(buf, HTTP_SERVER_STRING);
  293.     W5500_send(s_client, buf, strlen(buf));

  294.     sprintf(buf, "Content-Type: text/html\r\n");
  295.     W5500_send(s_client, buf, strlen(buf));

  296.     sprintf(buf, "Content-Length: %d\r\n", html_file_len);
  297.     W5500_send(s_client, buf, strlen(buf));

  298.     sprintf(buf, "\r\n");
  299.     W5500_send(s_client, buf, strlen(buf));

  300.     if ( html_data != NULL && html_file_len > 0 )
  301.     {
  302.         while ( html_send_pos < html_file_len )
  303.         {
  304.                 if ( html_send_pos + 500 < html_file_len )
  305.                 {
  306.                     cur_send_len =  W5500_send(s_client, html_data + html_send_pos, 500);
  307.                 }
  308.                 else
  309.                 {
  310.                     cur_send_len =  W5500_send(s_client, html_data + html_send_pos, html_file_len - html_send_pos );
  311.                 }

  312.                 if ( cur_send_len == 0 )
  313.                 {
  314.                         break;
  315.                 }

  316.                 html_send_pos += cur_send_len;
  317.         }
  318.     }
  319. }

  320. void server_respond(s_client)
  321. {
  322.          char      buf[1024];
  323.          uint16_t  html_file_len = 0;
  324.          uint16_t  html_send_pos = 0;
  325.          uint16_t  cur_send_len = 0;

  326.         //发送HTTP头
  327.          sprintf(buf, "HTTP/1.0 200 OK\r\n");
  328.     W5500_send(s_client, buf, strlen(buf));

  329.     sprintf(buf, HTTP_SERVER_STRING);
  330.     W5500_send(s_client, buf, strlen(buf));

  331.     sprintf(buf, "Content-Type: text/html\r\n");
  332.     W5500_send(s_client, buf, strlen(buf));

  333.     sprintf(buf, "\r\n");
  334.     W5500_send(s_client, buf, strlen(buf));


  335.     /*
  336.      *
  337.     sprintf(buf, "<HTML><HEAD><TITLE>MESH Node Configuration Site</TITLE></HEAD>\r\n");
  338.     W5500_send(s_client, buf, strlen(buf));


  339.     sprintf(buf, "<BODY bgcolor=\"BLUE\">\r\n");
  340.     W5500_send(s_client, buf, strlen(buf));

  341.     sprintf(buf, "<form action=\"/config\" method=\"POST\">用户名:<input type=\"text\" name=\"test_name\"><br /><br />密  码:<input type=\"text\" name=\"test_pwd\"><br /><br /><input type=\"submit\"><form>\r\n");
  342.     W5500_send(s_client, buf, strlen(buf));


  343.     sprintf(buf, "</BODY></HTML>\r\n");
  344.     W5500_send(s_client, buf, strlen(buf));
  345.      * */

  346.     html_file_len = sizeof(g_HTML_index);

  347.     while ( html_send_pos < html_file_len )
  348.     {
  349.             if ( html_send_pos + 500 < html_file_len )
  350.             {
  351.                 cur_send_len =  W5500_send(s_client, g_HTML_index + html_send_pos, 500);
  352.             }
  353.             else
  354.             {
  355.                 cur_send_len =  W5500_send(s_client, g_HTML_index + html_send_pos, html_file_len - html_send_pos );
  356.             }

  357.             html_send_pos += cur_send_len;
  358.     }

  359. }

  360. bool http_process_request(SOCKET s_client, char* URI, char** param_list, uint16_t param_num )
  361. {
  362.         bool result = false;


  363.     if ( strcasecmp(URI, "/") == 0 )
  364.     {
  365.             server_respond(s_client);
  366.             result = true;
  367.     }
  368.     else if ( strcasecmp(URI, "/config") == 0 )
  369.     {
  370.             //result = true;
  371.          }



  372.         return result;
  373. }





  374. /**********************************************************************
  375. * 服务器的loop
  376. **********************************************************************/
  377. void HTTP_loop()
  378. {
  379.         char*     requeset_header_item = NULL;
  380.         char*     requeset_header_item_value = NULL;
  381.         uint16_t  cur_request_line_len = 0;
  382.         bool      result = false;

  383.         /* 如果缓冲包达到最大处理长度,则直接返回错误,关闭连接 */
  384.         if ( l_request_length == HTTP_REQUEST_BUFF_SIZE )
  385.         {
  386.              /* 格式错误,请求包数据超过处理上限 */
  387.             http_respond_error(l_http_socket, 400);
  388.                  W5500_socket_TCP_close(l_http_socket);
  389.                  reset_request();
  390.                  return;
  391.         }

  392.         /*  判断当前是否正在接收正文  */
  393.         if ( l_request_content_len_desired > 0 )
  394.         {
  395.                 /* 正在接收正文,此时仅增加正文长度即可 */
  396.                 l_request_content_len += l_request_length - l_cur_process_pos;
  397.                 l_cur_process_pos = l_request_length;

  398.                 if ( l_request_content_len < l_request_content_len_desired )
  399.                 {
  400.                         /* 还没达到预期的正文长度,结束本次循环,待下次处理 */
  401.                         return;
  402.                 }
  403.                 else if ( l_request_content_len > l_request_content_len_desired )
  404.                 {
  405.                     /* 格式错误,实际接收的正文长度已经超过请求头中的正文长度 */
  406.                            http_respond_error(l_http_socket, 400);
  407.                          W5500_socket_TCP_close(l_http_socket);
  408.                          reset_request();
  409.                          return;
  410.                 }
  411.                 else
  412.                 {
  413.                         /* 实际接收的正文长度与请求头中的正文长度一致,则继续处理 */
  414.                         result = true;
  415.                 }
  416.         }
  417.         else
  418.         {
  419.                 /* 把需要的信息全都解析出来 */
  420.                  while ( l_cur_process_pos < l_request_length )
  421.                 {
  422.                          /* 提取一行,获得该行长度  */
  423.                          cur_request_line_len = get_line( l_request_buffer + l_cur_process_pos, l_request_length - l_cur_process_pos );
  424.                         if ( cur_request_line_len > 2 )
  425.                         {
  426.                                 /* 如果是第一行,则解析方法、URL和版本号 */
  427.                                 if ( l_cur_process_pos == 0 )
  428.                                 {
  429.                                         parse_request_first(l_request_buffer, cur_request_line_len, &l_request_method, &l_request_URL, &l_request_version);
  430.                                 }
  431.                                 else
  432.                                 {
  433.                                         /* 解析请求头信息 */
  434.                                         parse_request_header( l_request_buffer + l_cur_process_pos, cur_request_line_len, &requeset_header_item, &requeset_header_item_value );
  435.                                         if( requeset_header_item != NULL && requeset_header_item_value != NULL )
  436.                                         {
  437.                                                 /* 必须至少解析正文长度 */
  438.                                                 if ( strcasecmp(requeset_header_item, "Content-Length") == 0 )
  439.                                                 {
  440.                                                         sscanf(requeset_header_item_value, "%hu", &l_request_content_len_desired);
  441.                                                 }
  442.                                         }
  443.                                 }

  444.                                 l_cur_process_pos += cur_request_line_len;
  445.                         }
  446.                         else if ( cur_request_line_len == 2 ) /* 这是一个空行,默认有消息正文 */
  447.                         {
  448.                                 l_cur_process_pos += cur_request_line_len;

  449.                                 /* 如果空行之后还有数据,那就是正文  */
  450.                                 if ( l_cur_process_pos < l_request_length )
  451.                                 {
  452.                                         l_request_content = (char *)(l_request_buffer + l_cur_process_pos );
  453.                                         l_request_content_len = l_request_length - l_cur_process_pos;
  454.                                 }
  455.                         }
  456.                         else
  457.                         {
  458.                                 /* 没有找到行,那说明已经达到最末端 */
  459.                                 l_cur_process_pos = l_request_length;
  460.                         }
  461.                 }

  462.                  if ( l_request_content_len > 0 )
  463.                  {
  464.                          if ( l_request_content_len_desired == 0 )
  465.                          {
  466.                               /* 格式错误,请求头未包含正文数据长度 */
  467.                              http_respond_error(l_http_socket, 400);
  468.                                   W5500_socket_TCP_close(l_http_socket);
  469.                                   reset_request();
  470.                                   return;
  471.                          }
  472.                          else
  473.                          {
  474.                                  if ( l_request_content_len < l_request_content_len_desired )
  475.                                  {
  476.                                          /* 直接结束本次循环,待下次处理 */
  477.                                          return;
  478.                                  }
  479.                                  else if ( l_request_content_len > l_request_content_len_desired )
  480.                                  {
  481.                                       /* 格式错误,实际接收的正文长度已经超过请求头中的正文长度 */
  482.                                      http_respond_error(l_http_socket, 400);
  483.                                           W5500_socket_TCP_close(l_http_socket);
  484.                                           reset_request();
  485.                                           return;
  486.                                  }
  487.                                  else
  488.                                  {
  489.                                          /* 实际接收的正文长度与请求头中的正文长度一致,则继续处理 */
  490.                                         result = true;
  491.                                  }
  492.                          }
  493.                  }
  494.                  else
  495.                  {
  496.                          if ( l_request_content_len_desired > 0 )
  497.                          {
  498.                               /* 格式错误,请求头中的正文长度 不为0,但接收的正文长度为0 */
  499.                              http_respond_error(l_http_socket, 400);
  500.                                   W5500_socket_TCP_close(l_http_socket);
  501.                                   reset_request();
  502.                                   return;
  503.                          }
  504.                          else
  505.                          {
  506.                                  /* 实际接收的正文长度与请求头中的正文长度一致,则继续处理 */
  507.                                 result = true;
  508.                          }
  509.                  }
  510.         }

  511.         /* 进行http响应,必须有请求数据,并且正文接收处理完成 */
  512.         if ( result && l_request_length > 0 )
  513.         {
  514.                  /* 判断方法、URL和版本号是否正确 */
  515.                  if ( l_request_method != NULL && strlen(l_request_method) > 0 &&
  516.                                  l_request_URL != NULL && strlen(l_request_URL) > 0 &&
  517.                                 l_request_version != NULL && strlen(l_request_version) > 0 )
  518.                  {
  519.                          if ( strcasecmp(l_request_version, "HTTP/0.9") == 0 ||
  520.                                   strcasecmp(l_request_version, "HTTP/1.0") == 0 ||
  521.                                  strcasecmp(l_request_version, "HTTP/1.1") == 0 )
  522.                          {
  523.                              /* 解析URL,获取URI,获取参数 */
  524.                                  l_request_param_num = parse_request_URL((uint8_t *)l_request_URL, &l_request_URI, l_request_param_list, HTTP_REQUEST_PARAM_MAX);

  525.                              /* 根据解析结果进行处理 */
  526.                               if ( strcasecmp(l_request_method, "GET") == 0 )
  527.                                    {
  528.                                       result = true;
  529.                                    }
  530.                               else if ( strcasecmp(l_request_method, "POST") == 0 )
  531.                               {
  532.                                       /* 解析正文,获取参数 */
  533.                                       l_request_param_num = parse_request_parameter((uint8_t *)l_request_content, l_request_content_len, l_request_param_list, HTTP_REQUEST_PARAM_MAX);
  534.                                       result = true;
  535.                               }
  536.                               else
  537.                               {
  538.                                       result = false;
  539.                               }

  540.                               /* 可以处理 */
  541.                               if ( result )
  542.                               {
  543.                                       result = http_process_request(l_http_socket, l_request_URI, l_request_param_list, l_request_param_num);
  544.                                        if ( !result )
  545.                                       {
  546.                                               /* 没有发现资源 */
  547.                                                http_respond_error(l_http_socket, 501);
  548.                                       }
  549.                               }
  550.                               else
  551.                               {
  552.                                        /* 仅仅实现了GET和POST */
  553.                                       http_respond_error(l_http_socket, 501);
  554.                               }
  555.                          }
  556.                          else
  557.                          {
  558.                               /* 不支持的版本 */
  559.                              http_respond_error(l_http_socket, 505);
  560.                          }
  561.                  }
  562.                  else
  563.                  {
  564.                      /* 格式错误 */
  565.                     http_respond_error(l_http_socket, 400);
  566.                  }

  567.             /* 传输完成后,必须要切断连接 */
  568.                  W5500_socket_TCP_close(l_http_socket);
  569.                  reset_request();
  570.         }
  571. }




  572. /**********************************************************************
  573. * 初始化,主程序调用
  574. **********************************************************************/
  575. void HTTP_init(SOCKET s)
  576. {
  577.         reset_request();

  578.         if ( W5500_socket_TCP_Server( s, HTTP_SERVER_PORT_DEFAULT, on_http_request ) )
  579.         {
  580.                 W5500_socket_TCP_listen( s );
  581.                 l_http_socket = s;
  582.         }
  583. }
复制代码

所有资料51hei提供下载:
httpd.rar (4.62 KB, 下载次数: 8)


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:21783 发表于 2019-10-17 11:44 | 只看该作者
谢谢分享~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表