博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

  • 本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。

1、 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 “Hellow World”。
2、 巧用eclipes编辑器,官方教程在在Windows下搭建esp32开发环境,打印 “Hellow World”。
3、 认识基本esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。
4、体会esp32的强大的定时器功能, 实现定时2s闪烁一盏LED灯。
5、接触实践esp32的pwm宽度脉冲功能, 实现呼吸效果闪烁一盏LED灯。
6、smartConfig和微信airKiss在esp32的实现,一键配网轻松快捷连接路由器。
7、利用GPIO中断做一个按键的短按和长按的回调事件,再也无须担心触发源。
8、esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。
9、esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。
10、乐鑫esp32 SDK编程利用rmt驱动ws2812七彩灯,实现彩虹渐变效果。
11、入门 乐鑫esp-adf 音频框架开发,esp32造一个蓝牙耳机,实现切换歌曲,获取歌曲信息等功能。
12、开源一个微信公众号airkiss配网esp32以及局域网发现功能的工程,分享一个airkiss配网小工具。
13、esp32 内置 dns 服务器,无需外网访问域名返回指定网页。
14、esp32 sdk编程实现门户强制认证,连接esp32热点之后,自动强制弹出指定的登录界面。
15、认识本地离线语音唤醒识别框架 esp-skainet ,实现较低成本的硬件语音本地识别控制。
16、学习本地语音唤醒离线识别框架 esp-skainet ,如何修改唤醒词? 如何自定义命令词?如何做意图动作?
17、全网首发,乐鑫esp32 sdk直连京东微联·小京鱼 · IoT开放平台,实现叮咚音响语音智能控制。


在这里插入图片描述

一、前言


     在物联网搞了这么多的玩意,包括服务器、嵌入式、小程序、Android等,我的确是不会 ‘精通’ 一门技术,看到网上很多招聘信息,嵌入式开发的工资较低的,其中 Android和服务器这边薪酬较高,我在想,如果我哪一天去面试新公司,我该面试哪个职位?真不知道。。。

     随着年龄成长,成家立业的想法迫使我压力很大,也许你到了这30未至,未成家的阶段,和我一样的焦虑。好吧!说到底,还是工作与生活,明年准备换个公司吧。希望身边都是优秀的人,待遇薪酬也高一点!

     说了这么多,开始正题:如何没有外网的情况下,实现访问域名返回指定的网页!

二、实现原理和流程


     也许你会问,这个无需外网访问域名就可以返回 html 有啥用啊?这个问题提的好,我们可以在平时的生活中也遇到这样的事情:第一次买的路由器,需要配置 网络运营商的 账号密码,我们一般是 浏览器地址栏输入“192.168.xx.xx” ,而有的是 访问 “www.xxx.com” 这样的域名。你觉得哪个好?我觉得域名比较好;

  • 第一:体现你的品牌,比如小米的 mi.wifi.com 这样的域名;
  • 第二:主机名便于人的记忆,而IP地址便于计算机网络设备的处理;

2.1 原理

     因为没外网访问域名,所以需要内置一个 dns 服务器,毕竟我们平时访问的域名,都是先经过 dns 解析得到 ip 地址再访问 ip 地址的,就像前几天的国内码云域名被阿里云冻结事件,不难分析出,如果你的域名无法被解析,那么客户端无法访问;

     有一个dns服务器还是不够的,因为你即使得到 ip 地址,客户端还会去 get 请求这个域名,所以,你还得具备一个 server 服务器,而 server 服务器是基于 tcp 的,所以你得做一个 tcp 服务器 http 格式,端口号是 80 ;

     而DNS走的是UDP协议,使用的端口号是53,所以不管收到什么,都回复!

     dns服务器建立参考:传送门


2.2 流程


     第一步自己开启热点,这时候你就是相当于一个没外网的一个路由器啦!之后开启 dns 服务器,再开启 tcp 服务器,等待客户端 dns 解析请求和 http 请求 !


在这里插入图片描述

三、代码


  • 所有代码,都在博文下面获取!

3.1 DNS服务器

  • 得到一个 dns 解析请求:
void get_dns_request(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    printf("get the massage from sat\n");
    struct dns_ans_hdr hdr;
    upcb1 = upcb;
    addr1 = addr;
    port1 = port;
    if (p->tot_len < (SIZEOF_DNSANS_HDR + SIZEOF_DNSANS_HDRQUE))
    {
        printf("dns_recv: pbuf too small\n");
        /* 非dns请求 */
    }
    else
    {
        pbuf_copy_partial(p, &hdr, SIZEOF_DNSANS_HDR, 0);
        txid = ntohs(hdr.id);
        nquestions = ntohs(hdr.numquestions);
    }
    pbuf_free(p); //check this
    dns_server_send();
}

  • 返回 dns 解析请求给客户端,下面我是指为一个域名,宏定义的 TABLENAME 为: www.xuhong.com,这样的格式!
void dns_server_send(void)
{
    struct pbuf *rp = NULL;
    struct dns_ans_hdr hdr;
    struct dns_ans_ans qry;
    uint8_t n;
    uint16_t query_idx, copy_len;
    const char *hostname, *hostname_part;
    struct dns_table_entry dns_server_table = {
        .txid = DNS_SERVER_ID,
        .flags = DNS_SERVER_FLAGS,
        .numque = DNS_SERVER_NUMQUE,
        .ansrrs = DNS_SERVER_ANSRRS,
        .autrrs = DNS_SERVER_AUTRRS,
        .addrrs = DNS_SERVER_ADDRRS,
        .name = {0},
        .type = DNS_SERVER_TYPE,
        .class = DNS_SERVER_CLASS,
        .poiname = DNS_POINAME,
        .anstype = DNS_SERVER_ANSTYPE,
        .anstypee = DNS_SERVER_ANSTYPEE,
        .datalen = DNS_SERVER_DATALEN,
        .anstime = DNS_SERVER_ANSTIME,
        .adress = DNS_SERVER_ADRESS};
    strcpy(dns_server_table.name, TABLENAME); // TABLENAME是我指定的域名
    struct dns_table_entry *entry = &dns_server_table;
    rp = pbuf_alloc(PBUF_TRANSPORT, 51, PBUF_RAM);
    if (rp != NULL)
    {
        memset(&hdr, 0, SIZEOF_DNSANS_HDR);
        /* fill dns_ans header */
        hdr.id = htons(txid);
        hdr.flag = htons(entry->flags);
        hdr.numquestions = htons(entry->numque);
        hdr.numanswers = htons(entry->ansrrs);
        hdr.numauthrr = htons(entry->autrrs);
        hdr.numextrarr = htons(entry->addrrs);
        pbuf_take(rp, &hdr, SIZEOF_DNSANS_HDR);
        /* convert hostname into suitable query format. */
        hostname = entry->name;
        --hostname;
        query_idx = SIZEOF_DNSANS_HDR;
        do
        {
            ++hostname;
            hostname_part = hostname;
            for (n = 0; *hostname != '.' && *hostname != 0; ++hostname)
            {
                ++n;
            }
            copy_len = (u16_t)(hostname - hostname_part);
            pbuf_put_at(rp, query_idx, n);
            pbuf_take_at(rp, hostname_part, copy_len, query_idx + 1);
            query_idx += n + 1;
        } while (*hostname != 0);
        pbuf_put_at(rp, query_idx, 0);
        query_idx++;
        /* fill dns ans */
        qry.typ = htons(entry->type);
        qry.cls = htons(entry->class);
        qry.point = htons(entry->poiname);
        qry.antyp = htons(entry->anstype);
        qry.antypp = htons(entry->anstypee);
        qry.len = htons(entry->datalen);
        qry.time = htonl(entry->anstime);
        qry.addr = htonl(entry->adress);
        pbuf_take_at(rp, &qry, SIZEOF_DNSANS_HDRQUE, query_idx);
        udp_sendto(upcb1, rp, addr1, port1);
        free(rp);
    }
}

3.2 server服务器

  • 下面代码演示了如何作为一个 http 格式返回给客户端,其中 http_html_hdr 是头部 header ,http_index_hml 是body 部分,部分浏览器可能头部还需要添加 Content-Length 作为返回body长度才会正常显示,这个您自己添加吧!我就不加了!
  • 如果你要修改返回的内容,可以修改下面的 http_index_hml;
const static char http_html_hdr[] =
    "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n";
const static char http_index_hml[] = "<!DOCTYPE html>"
      "<html>\n"
      "<head>\n"
      "  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
      "  <style type=\"text/css\">\n"
      "    html, body, iframe { margin: 0; padding: 0; height: 100%; }\n"
      "    iframe { display: block; width: 100%; border: none; }\n"
      "  </style>\n"
      "<title>HELLO ESP32</title>\n"
      "</head>\n"
      "<body>\n"
      "<h1>Hello World, from ESP32!</h1>\n"
      "</body>\n"
      "</html>\n";

static void web_http_server(struct netconn *conn)
{
  struct netbuf *inputbuf;
  char *buf;
  u16_t buflen;
  err_t err;

  err = netconn_recv(conn, &inputbuf);
  if (err == ERR_OK) {
    netbuf_data(inputbuf, (void**)&buf, &buflen);
    printf("the received data:\n%s\n",buf);
    /* Judge if this is an HTTP GET command */
    if (buflen >= 5 && buf[0] == 'G' && buf[1] == 'E' && buf[2] == 'T' && buf[3] == ' ' && buf[4] == '/' ) {
      netconn_write(conn, http_html_hdr, sizeof(http_html_hdr)-1, NETCONN_NOCOPY);
      netconn_write(conn, http_index_hml, sizeof(http_index_hml)-1, NETCONN_NOCOPY);
      }
      else {
          netconn_write(conn, http_index_hml, sizeof(http_index_hml)-1, NETCONN_NOCOPY);
      }
    }
  }
  netconn_close(conn);
  netbuf_delete(inputbuf);
}

四、其他


  • 代码编译成功之后,手机连接热点 XUHONG-DNS , 密码是 12345678 ,然后浏览器访问 www.xuhong.com 即可看到效果!
源码学习与总结,参考来自:
  • 强制门户认证:https://www.jianshu.com/p/0293e1a30b89
  • 乐鑫:https://github.com/WeldonWangwang/

本博文代码下载(不再需要积分下载,免费给大家使用)

https://github.com/xuhongv/StudyInEsp32/tree/master/12_dns_server

另外,不要把我的博客作为学习标准,我的只是笔记,难有疏忽之处,如果有,请指出来,也欢迎留言哈!

  • 玩转esp8266带你飞、加群付费QQ群,不喜的朋友勿喷勿加:434878850
  • esp8266源代码学习汇总(持续更新,欢迎star):https://github.com/xuhongv/StudyInEsp8266
  • esp32源代码学习汇总(持续更新,欢迎star):https://github.com/xuhongv/StudyInEsp32
  • 关注下面微信公众号二维码,干货多多,第一时间推送!