欢迎光临散文网 会员登陆 & 注册

17-对part15-tcpip-webserver的翻译和搬运

2023-08-20 16:58 作者:Xdz-2333  | 我要投稿

非黑色字体均为我自己添加

图均为原文所有

原文放在文章末尾

加入TCP/IP栈

        从我们的part14-spi-ethernet 中的以太网模块达成了达成了"证明生活"的成就,你毫无疑问会好奇如何从这里出发到服务器上的网页,在推特上发推(可惜现在叫X.com了)或者只是相应一个ping!

        这就是你需要成熟的TCP/IP栈的地方,它超越了手工的ARP,实现了许多协议来达成高效的双向通信.

        在这个部分里我们利用一些 tuxgraphics.org中Guido Socher的代码.它被设置为具有轻量级的针对嵌入式设备的TCP/IP栈.我选择这个是因为使它运转(或者"移植")起来非常的简单,但你可能想要看看LwIP(https://en.wikipedia.org/wiki/LwIP)如果你希望一些高级功能.

代码

        大多数代码在  tcpip/ 子文件夹里.我实际上在这个tarball(http://tuxgraphics.org/common/src2/article09051/eth_tcp_client_server-dhcp-5.10.tar.gz)里面遇到了它,再次做了一些不重要的改动( diff 是你的好朋友!).

        它确实要求我暴露我在 lib/fb.c 中实现的strlen()函数,所以它被添加到 include/fb.h 中.类似的,我们暴露在 kernel/kernel.c 中实现的 memcpy() 函数,所以它被添加到 kernel/kernel.h 中.

        我同样需要一个单独的函数来告诉ENC发送一个包.没有什么新东西,只是不同的封装:

void enc28j60PacketSend(unsigned short buflen, void *buffer) {

   if (ENC_RestoreTXBuffer(&handle, buflen) == 0) {

      ENC_WriteBuffer((unsigned char *) buffer, buflen);

      handle.transmitLength = buflen;

      ENC_Transmit(&handle);

   }

}

        它同样被添加到 kernel/kernel.h 中了.

arp.c发生了什么?

        你会注意到我合并了 arp.c 和 kernel.c.我也没有使用多核或IRQ定时器,以保持内核的简单性.我们仍然用同样的方法初始化网卡,但是当我们完成时我们调用 Guido 中的代码:

init_udp_or_www_server(myMAC, deviceIP);

        它告诉TCP/IP库我们是谁,所以我们在同一页!

        最后,除了清理之外(比如移动 HAL/系统 时钟到 lib/io.c,同时 include/io.h 也对应改变),主要的变化是新的 serve()  函数:

void serve(void)

{

   while (1) {

      while (!ENC_GetReceivedFrame(&handle));


      uint8_t *buf = (uint8_t *)handle.RxFrameInfos.buffer;

      uint16_t len = handle.RxFrameInfos.length;

      uint16_t dat_p = packetloop_arp_icmp_tcp(buf, len);


      if (dat_p != 0) {

         debugstr("Incoming web request... ");


         if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0) {

            debugstr("not GET");

            dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>ERROR</h1>");

         } else {

            if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) {

               // just one web page in the "root directory" of the web server

               debugstr("GET root");

               dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello world!</h1>");

            } else {

               // just one web page not in the "root directory" of the web server

               debugstr("GET not root");

               dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Goodbye cruel world.</h1>");

            }

         }


         www_server_reply(buf, dat_p); // send web page data

         debugcrlf();

      }

   }

}

        这是一个无限循环,它等待将要到来的包然后首先把它传递到 Guido 的 packetloop_arp_icmp_tcp() 函数.这个函数实现了一些有用的东西,比如应答ping.我修改这个例程当它发送"pong"的时候把信息打印到屏幕上(看到 tcpip/ip_arp_udp_tcp.c 的第 1371 行),所以当它行动时我们可以看到!

        检查 packetloop_arp_icmp_tcp() 的返回值,然后允许我们检查它是否是一个到来的网络请求,因为我们已经使用 #define WWW_server 在 tcpip/ip_config.h中设置TCP/IP 库为一个网络服务器.

        我们基于三中可能的情况做出服务应答:

  • 到来的请求不是一个GET请求(比如它可能是一个HEAD请求) -- 你可以使用 curl 工具模拟这个:curl -I 192.168.0.66

  • 到来的请求是一个要求网页根目录的GET请求  / -- curl 192.168.0.66/

  • 到来的请求是任何一个非根网页目录请求 -- 比如 curl 192.168.0.66/isometimes/monkey

        我推荐阅读这一页(http://tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml)来得到完全的解释.我移植的代码与这里你看到的非常相似.

        想象一下,当我构建、运行并ping到192.168.0.66的RPi4,并在我的笔记本电脑和iPhone上的浏览器上获得web响应时,我有多兴奋!

作者iPhone上接收到的信息
作者电脑上接收到的信息


原文如下

Adding a TCP/IP stack

---------------------

Having achieved "proof of life" from our Ethernet module in _part14-spi-ethernet_, you're doubtless wondering how to go from there to serving web pages, posting tweets on Twitter or perhaps even just simply responding to a ping!


This is where you'll need a fully-fledged TCP/IP stack that goes way beyond handcrafted ARPs, implementing many more protocols to achieve efficient bi-directional communication.


In this part we make use of some code from Guido Socher of [tuxgraphics.org](http://tuxgraphics.org/), designed to be a lightweight TCP/IP stack for embedded devices. I chose this because it was super simple to get working (or "port"), but you might want to look at [LwIP](https://en.wikipedia.org/wiki/LwIP) if you need something more advanced.


The code

--------

Most of the new code is in the _tcpip/_ subdirectory. I actually came across it in [this tarball](http://tuxgraphics.org/common/src2/article09051/eth_tcp_client_server-dhcp-5.10.tar.gz) and, again, made only a very few cosmetic changes (`diff` is your friend!).


It did require me to expose the `strlen()` function we implemented in _lib/fb.c_, so that's added to _include/fb.h_. Similarly, we expose the `memcpy()` function we implemented in _kernel/kernel.c_, so that's added to _kernel/kernel.h_.


I also needed a single function that tells the ENC to send a packet. Nothing new here, just different packaging:


```c

void enc28j60PacketSend(unsigned short buflen, void *buffer) {

   if (ENC_RestoreTXBuffer(&handle, buflen) == 0) {

      ENC_WriteBuffer((unsigned char *) buffer, buflen);

      handle.transmitLength = buflen;

      ENC_Transmit(&handle);

   }

}

```


This was also added to _kernel/kernel.h_.


What happened to _arp.c_?

-------------------------

You'll notice that I've merged _arp.c_ and _kernel.c_. I have also gone away from doing anything wih multicore or IRQ timers to keep this kernel simple. We still initialise the network card in exactly the same way but, when we're done, we call this function in Guido's code:


```c

init_udp_or_www_server(myMAC, deviceIP);

```


This tells the TCP/IP library who we are, so we're all on the same page!


Finally, and aside from a little cleanup (eg. moving the HAL/system timer functions to _lib/io.c_ with the commensurate changes to _include/io.h_), the major change is the new `serve()` function:


```c

void serve(void)

{

   while (1) {

      while (!ENC_GetReceivedFrame(&handle));


      uint8_t *buf = (uint8_t *)handle.RxFrameInfos.buffer;

      uint16_t len = handle.RxFrameInfos.length;

      uint16_t dat_p = packetloop_arp_icmp_tcp(buf, len);


      if (dat_p != 0) {

         debugstr("Incoming web request... ");


         if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0) {

            debugstr("not GET");

            dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>ERROR</h1>");

         } else {

            if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) {

               // just one web page in the "root directory" of the web server

               debugstr("GET root");

               dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello world!</h1>");

            } else {

               // just one web page not in the "root directory" of the web server

               debugstr("GET not root");

               dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Goodbye cruel world.</h1>");

            }

         }


         www_server_reply(buf, dat_p); // send web page data

         debugcrlf();

      }

   }

}

```


This is an infinite loop which waits for an incoming packet and then firstly passes it to Guido's `packetloop_arp_icmp_tcp()` function. This function implements some useful things, like responding to pings. I modified the routine to print a message to the screen when it sends a "pong" (look from line 1371 of _tcpip/ip_arp_udp_tcp.c_), so we can see when it's in action!


Examining the return value of `packetloop_arp_icmp_tcp()` then allows us to check whether there is an incoming web request, since we've configured the TCP/IP library to be a web server in _tcpip/ip_config.h_ with `#define WWW_server`.


We then serve responses based on three possible cases:


 * The incoming request is not a GET request (eg. maybe it's a HEAD request) - you can simulate this using the `curl` tool: `curl -I 192.168.0.66`

 * The incoming request is a GET request for the root web page `/` - `curl 192.168.0.66/`

 * The incoming request is a GET request for any non-root web page - eg. `curl 192.168.0.66/isometimes/monkey`


I recommend reading [this page](http://tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml) for a full explanation. The code I have ported is very similar to what you see there.


_Imagine my excitement when I built, ran and could ping my RPi4 at 192.168.0.66 and get a web response to my browser on both my laptop and my iPhone!_


![Pinging from my iPhone](images/15-tcpip-webserver-pinging.jpg)

![Browsing from my laptop](images/15-tcpip-webserver-browser.png)



17-对part15-tcpip-webserver的翻译和搬运的评论 (共 条)

分享到微博请遵守国家法律