【Mongoose笔记】HTTP 客户端

【Mongoose笔记】HTTP 客户端

简介

Mongoose 笔记系列用于记录学习 Mongoose 的一些内容。

Mongoose 是一个 C/C++ 的网络库。它为 TCP、UDP、HTTP、WebSocket、MQTT 实现了事件驱动的、非阻塞的 API。

项目地址:

https://github.com/cesanta/mongoose

学习

下面通过学习 Mongoose 项目代码中的 http-client 示例程序 ,来学习如何使用 Mongoose 实现一个简单的 HTTP 客户端程序。使用树莓派平台进行开发验证。

http-client 的示例程序不长,代码如下:

// Copyright (c) 2021 Cesanta Software Limited
// All rights reserved
//
// Example HTTP client. Connect to `s_url`, send request, wait for a response,
// print the response and exit.
// You can change `s_url` from the command line by executing: ./example YOUR_URL
//
// To enable SSL/TLS, make SSL=OPENSSL or make SSL=MBEDTLS

#include "mongoose.h"

// The very first web page in history. You can replace it from command line
static const char *s_url = "http://info.cern.ch/";
static const char *s_post_data = NULL;      // POST data
static const uint64_t s_timeout_ms = 1500;  // Connect timeout in milliseconds

// Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
  if (ev == MG_EV_OPEN) {
    // Connection created. Store connect expiration time in c->label
    *(uint64_t *) c->label = mg_millis() + s_timeout_ms;
  } else if (ev == MG_EV_POLL) {
    if (mg_millis() > *(uint64_t *) c->label &&
        (c->is_connecting || c->is_resolving)) {
      mg_error(c, "Connect timeout");
    }
  } else if (ev == MG_EV_CONNECT) {
    // Connected to server. Extract host name from URL
    struct mg_str host = mg_url_host(s_url);

    // If s_url is https://, tell client connection to use TLS
    if (mg_url_is_ssl(s_url)) {
      struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host};
      mg_tls_init(c, &opts);
    }

    // Send request
    int content_length = s_post_data ? strlen(s_post_data) : 0;
    mg_printf(c,
              "%s %s HTTP/1.0\r\n"
              "Host: %.*s\r\n"
              "Content-Type: octet-stream\r\n"
              "Content-Length: %d\r\n"
              "\r\n",
              s_post_data ? "POST" : "GET", mg_url_uri(s_url), (int) host.len,
              host.ptr, content_length);
    mg_send(c, s_post_data, content_length);
  } else if (ev == MG_EV_HTTP_MSG) {
    // Response is received. Print it
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    printf("%.*s", (int) hm->message.len, hm->message.ptr);
    c->is_closing = 1;         // Tell mongoose to close this connection
    *(bool *) fn_data = true;  // Tell event loop to stop
  } else if (ev == MG_EV_ERROR) {
    *(bool *) fn_data = true;  // Error, tell event loop to stop
  }
}

int main(int argc, char *argv[]) {
  const char *log_level = getenv("LOG_LEVEL");  // Allow user to set log level
  if (log_level == NULL) log_level = "4";       // Default is verbose

  struct mg_mgr mgr;              // Event manager
  bool done = false;              // Event handler flips it to true
  if (argc > 1) s_url = argv[1];  // Use URL provided in the command line
  mg_log_set(atoi(log_level));    // Set to 0 to disable debug
  mg_mgr_init(&mgr);              // Initialise event manager
  mg_http_connect(&mgr, s_url, fn, &done);  // Create client connection
  while (!done) mg_mgr_poll(&mgr, 50);      // Event manager loops until 'done'
  mg_mgr_free(&mgr);                        // Free resources
  return 0;
}

下面从main函数开始分析代码。

首先是获取用户需要设置的日志等级,如不存在则默认为 4。

  const char *log_level = getenv("LOG_LEVEL");  // Allow user to set log level
  if (log_level == NULL) log_level = "4";       // Default is verbose

然后是一些变量定义。其中struct mg_mgr是用于保存所有活动连接的事件管理器。变量done用于判断何时停止下面的mg_mgr_poll函数的循环调用。

  struct mg_mgr mgr;              // Event manager
  bool done = false;              // Event handler flips it to true

如果命令行有传入 URL 参数,则使用命令行中提供的 URL 参数。

  if (argc > 1) s_url = argv[1];  // Use URL provided in the command line

如果命令行没有传入参数,则是使用全局变量s_url的默认 URL 参数值http://info.cern.ch/

// The very first web page in history. You can replace it from command line
static const char *s_url = "http://info.cern.ch/";

设置 Mongoose 日志记录级别,范围 0 - 4, 数值越大日志内容越多,设置 0 则为禁用调试信息。

  mg_log_set(atoi(log_level));    // Set to 0 to disable debug

初始化一个事件管理器,也就是将上面定义的struct mg_mgr变量 mgr 中的数据进行初始化。

  mg_mgr_init(&mgr);              // Initialise event manager

创建 HTTP 客户端连接,它分配所需的资源并启动连接过程。其中fn是事件处理函数。 这里还把变量done的地址给设置进去了,这个指针会在事件处理函数中作为参数fn_data传递,用于后续的断开连接设置。

  mg_http_connect(&mgr, s_url, fn, &done);  // Create client connection

这是事件循环,mg_mgr_poll 遍历所有连接,接受新连接,发送和接收数据,关闭连接,并为各个事件调用事件处理函数。

  while (!done) mg_mgr_poll(&mgr, 50);      // Event manager loops until 'done'

donetrue 时,也就是接收到了退出信号,则结束无限循环,调用 mg_mgr_free 关闭所有连接,释放所有资源。

  mg_mgr_free(&mgr);                        // Free resources

分析完main函数后,我们看下事件处理函数fn的代码。

判断是否接收到 MG_EV_OPEN 事件,收到MG_EV_OPEN 事件表示已创建连接。该事件在分配连接并将其添加到事件管理器之后立即发送。

在收到 MG_EV_OPEN 事件后,将连接过期时间存储在 c->label。其中mg_millis返回当前正常运行时间,s_timeout_ms表示超时时间。

  if (ev == MG_EV_OPEN) {
    // Connection created. Store connect expiration time in c->label
    *(uint64_t *) c->label = mg_millis() + s_timeout_ms;
  }

s_timeout_ms是一个全局变量,其默认值为 1500,也就是超时时间是 1500 毫秒。

static const uint64_t s_timeout_ms = 1500;  // Connect timeout in milliseconds

接下来是判断是否接收到 MG_EV_POLL 事件,在main函数中循环调用mg_mgr_poll 会发送MG_EV_POLL事件。

在收到MG_EV_POLL事件后判断是否连接超时。其中is_connecting表示正在进行非阻塞连接,is_resolving表示正在进行非阻塞DNS解析。

如果连接超时,则调用mg_error,会将MG_EV_ERROR发送到事件处理函数,并打印错误信息。

  } else if (ev == MG_EV_POLL) {
    if (mg_millis() > *(uint64_t *) c->label &&
        (c->is_connecting || c->is_resolving)) {
      mg_error(c, "Connect timeout");
    }
  }

接下来是判断是否接收到 MG_EV_CONNECT 事件,表示连接已建立。

首先使用mg_url_host函数从 URL 中提取主机名。然后使用mg_url_is_ssl函数用于检查给定的 URL 是否使用加密方案,如果s_urlhttps://,则告诉客户端连接使用 TLS,调用mg_tls_init函数初始化 TLS。

接着使用mg_printf发送请求,如果s_post_data数据不存在,则发送一个GET请求,如果s_post_data数据存在,则发送一个POST请求,并通过调用mg_send函数将s_post_data的数据附加到发送缓冲区中,只不过在这个示例程序中没有地方去给s_post_data赋予数据,所以必定为默认值NULLmg_printf中的mg_url_uri函数用于返回 URL 中的 URI 部分。

  } else if (ev == MG_EV_CONNECT) {
    // Connected to server. Extract host name from URL
    struct mg_str host = mg_url_host(s_url);

    // If s_url is https://, tell client connection to use TLS
    if (mg_url_is_ssl(s_url)) {
      struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host};
      mg_tls_init(c, &opts);
    }

    // Send request
    int content_length = s_post_data ? strlen(s_post_data) : 0;
    mg_printf(c,
              "%s %s HTTP/1.0\r\n"
              "Host: %.*s\r\n"
              "Content-Type: octet-stream\r\n"
              "Content-Length: %d\r\n"
              "\r\n",
              s_post_data ? "POST" : "GET", mg_url_uri(s_url), (int) host.len,
              host.ptr, content_length);
    mg_send(c, s_post_data, content_length);
  }

接下来是判断是否接收到 MG_EV_HTTP_MSG 事件,表示收到来自 HTTP 服务器端的应答。

将函数参数ev_data转换为 struct mg_http_message,其中包含已解析的 HTTP 应答。然后将收到的应答打印出来。

通过将is_closing设置为 1,来告诉 mongoose 将连接关闭,并将fn_data的值设置为true,也就是main函数中的变量done设置为true,让循环结束,停止mg_mgr_poll函数的事件循环调用。

 } else if (ev == MG_EV_HTTP_MSG) {
    // Response is received. Print it
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    printf("%.*s", (int) hm->message.len, hm->message.ptr);
    c->is_closing = 1;         // Tell mongoose to close this connection
    *(bool *) fn_data = true;  // Tell event loop to stop
  } 

接下来是判断是否接收到 MG_EV_ERROR 事件,表示出错,这里就是连接失败。来自于上面的MG_EV_POLL 事件处理。

连接失败后将将fn_data的值设置为true,停止main函数中的mg_mgr_poll函数的事件循环调用。

  } else if (ev == MG_EV_ERROR) {
    *(bool *) fn_data = true;  // Error, tell event loop to stop
  }

http-client 的示例程序代码就都解析完了,下面实际运行一下 http-client 程序。

打开示例程序,编译并运行:

pi@raspberrypi:~ $ cd Desktop/study/mongoose/examples/http-client/
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ make clean all
rm -rf example _CL* *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mbedtls
cc ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES  -o example main.c
./example 
18637 3 net.c:182:mg_connect            1 0xffffffff http://info.cern.ch/
18637 3 net.c:182:mg_connect            2 0xffffffff udp://8.8.8.8:53
18637 4 dns.c:251:mg_sendnsreq          1 resolving info.cern.ch @ 8.8.8.8, txnid 1
18637 3 sock.c:153:mg_send              2 0x4 0:0 30 err 0
18669 4 sock.c:648:mg_mgr_poll          2 -- tchrc
1866a 4 sock.c:648:mg_mgr_poll          1 -- tchRc
1869c 4 sock.c:648:mg_mgr_poll          2 -- tchrc
1869c 4 sock.c:648:mg_mgr_poll          1 -- tchRc
186ce 4 sock.c:648:mg_mgr_poll          2 -- tchrc
186ce 4 sock.c:648:mg_mgr_poll          1 -- tchRc
18700 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18700 4 sock.c:648:mg_mgr_poll          1 -- tchRc
18732 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18732 4 sock.c:648:mg_mgr_poll          1 -- tchRc
18764 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18764 4 sock.c:648:mg_mgr_poll          1 -- tchRc
18797 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18797 4 sock.c:648:mg_mgr_poll          1 -- tchRc
187c9 4 sock.c:648:mg_mgr_poll          2 -- tchrc
187c9 4 sock.c:648:mg_mgr_poll          1 -- tchRc
187cc 4 sock.c:648:mg_mgr_poll          2 r- tchrc
187cc 3 sock.c:281:read_conn            2 0x4 snd 0/0 rcv 0/2048 n=70 err=0
187cc 3 dns.c:167:dns_cb                1 webafs706.cern.ch is 188.184.21.108
187cc 3 sock.c:367:mg_connect_resolved  1 0x5 -> bcb8156c:80 pend
187cc 4 sock.c:648:mg_mgr_poll          1 -- tChrc
187fe 4 sock.c:648:mg_mgr_poll          2 -- tchrc
187ff 4 sock.c:648:mg_mgr_poll          1 -- tChrc
18831 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18831 4 sock.c:648:mg_mgr_poll          1 -- tChrc
18863 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18863 4 sock.c:648:mg_mgr_poll          1 -- tChrc
18895 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18895 4 sock.c:648:mg_mgr_poll          1 -- tChrc
188ba 4 sock.c:648:mg_mgr_poll          2 -- tchrc
188ba 4 sock.c:648:mg_mgr_poll          1 -w tChrc
188ba 4 sock.c:648:mg_mgr_poll          2 -- tchrc
188ba 4 sock.c:648:mg_mgr_poll          1 -w tchrc
188ba 3 sock.c:292:write_conn           1 0x5 snd 85/2048 rcv 0/0 n=85 err=115
188ec 4 sock.c:648:mg_mgr_poll          2 -- tchrc
188ec 4 sock.c:648:mg_mgr_poll          1 -- tchrc
1891e 4 sock.c:648:mg_mgr_poll          2 -- tchrc
1891e 4 sock.c:648:mg_mgr_poll          1 -- tchrc
18950 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18950 4 sock.c:648:mg_mgr_poll          1 -- tchrc
18983 4 sock.c:648:mg_mgr_poll          2 -- tchrc
18983 4 sock.c:648:mg_mgr_poll          1 -- tchrc
189a3 4 sock.c:648:mg_mgr_poll          2 -- tchrc
189a3 4 sock.c:648:mg_mgr_poll          1 r- tchrc
189a3 3 sock.c:281:read_conn            1 0x5 snd 0/2048 rcv 0/2048 n=878 err=115
HTTP/1.1 200 OK
Date: Wed, 16 Nov 2022 16:03:54 GMT
Server: Apache
Last-Modified: Wed, 05 Feb 2014 16:00:31 GMT
ETag: "286-4f1aadb3105c0"
Accept-Ranges: bytes
Content-Length: 646
Connection: close
Content-Type: text/html

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
<p>From here you can:</p>
<ul>
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
<li><a href="http://line-mode.cern.ch/www/hypertext/WWW/TheProject.html">Browse the first website using the line-mode browser simulator</a></li>
<li><a href="http://home.web.cern.ch/topics/birth-web">Learn about the birth of the web</a></li>
<li><a href="http://home.web.cern.ch/about">Learn about CERN, the physics laboratory where the web was born</a></li>
</ul>
</body></html>
189a3 3 net.c:159:mg_close_conn         1 0x5 closed
189a4 4 sock.c:648:mg_mgr_poll          2 -- tchrC
189a4 3 net.c:159:mg_close_conn         2 0x4 closed
189a4 3 net.c:244:mg_mgr_free           All connections closed
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ 

可以看到 HTTP 客户端访问http://info.cern.ch/网站的详细过程,包括从连接,发送,接收响应,关闭连接的过程。

默认情况下我们没有设置环境变量LOG_LEVEL,程序里会将日志记录级别设置为 4, 就向上面一样打印很多信息。

如果不想要那么多调试信息,可以通过设置环境变量LOG_LEVEL来调整日志等级,如下,我们将等级设置为 1 :

pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ export LOG_LEVEL=1; ./example
HTTP/1.1 200 OK
Date: Wed, 16 Nov 2022 16:12:50 GMT
Server: Apache
Last-Modified: Wed, 05 Feb 2014 16:00:31 GMT
ETag: "286-4f1aadb3105c0"
Accept-Ranges: bytes
Content-Length: 646
Connection: close
Content-Type: text/html

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
<p>From here you can:</p>
<ul>
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
<li><a href="http://line-mode.cern.ch/www/hypertext/WWW/TheProject.html">Browse the first website using the line-mode browser simulator</a></li>
<li><a href="http://home.web.cern.ch/topics/birth-web">Learn about the birth of the web</a></li>
<li><a href="http://home.web.cern.ch/about">Learn about CERN, the physics laboratory where the web was born</a></li>
</ul>
</body></html>
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ 

可以看到就没有那么多的调试信息了,只有 HTTP 响应消息。

下面我们来试着修改访问的 URL,去访问下我们自己的 HTTP 服务器。

我们先运行下 mongoose 的 http-server 的示例程序,然后再回来运行 http-client 程序,并将 URL 修改为http://localhost:8000,如下:

pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ ./example http://localhost:8000
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1948    

<!DOCTYPE html><html><head><title>Index of /</title><script>function srt(tb, sc, so, d) {var tr = Array.prototype.slice.call(tb.rows, 0),tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), t1 = a.cells[2].getAttribute('name'), t2 = b.cells[2].getAttribute('name'); return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : n1 ? parseInt(n2) - parseInt(n1) : c1.textContent.trim().localeCompare(c2.textContent.trim())); });for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); };window.onload = function() {var tb = document.getElementById('tb');var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];var sc = m[1], so = m[2]; document.onclick = function(ev) { var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); sc = c; ev.preventDefault();}};srt(tb, sc, so, true);}</script><style>th,td {text-align: left; padding-right: 1em; font-family: monospace; }</style></head><body><h1>Index of /</h1><table cellpadding="0"><thead><tr><th><a href="#" rel="0">Name</a></th><th><a href="#" rel="1">Modified</a></th><th><a href="#" rel="2">Size</a></th></tr><tr><td colspan="3"><hr></td></tr></thead><tbody id="tb">
  <tr><td><a href="..">..</a></td><td name=-1></td><td name=-1>[DIR]</td></tr>
  <tr><td><a href="example">example</a></td><td name=1667833757>2022/11/07 23:09:17</td><td name=144700>144700</td></tr>
  <tr><td><a href="Makefile">Makefile</a></td><td name=1667728719>2022/11/06 17:58:39</td><td name=1057>1057</td></tr>
  <tr><td><a href="main.c">main.c</a></td><td name=1667728719>2022/11/06 17:58:39</td><td name=3474>3474</td></tr>
  <tr><td><a href="README.md">README.md</a></td><td name=1667728719>2022/11/06 17:58:39</td><td name=68>68</td></tr>
</tbody><tfoot><tr><td colspan="3"><hr></td></tr></tfoot></table><address>Mongoose v.7.8</address></body></html>
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ 

可以看到成功得到 HTTP 响应消息,并且得到了访问的页面内容。另一边的 HTTP 服务器程序日志也显示收到了GET请求,并返回状态码 200, 实体主体的大小为 1948。

pi@raspberrypi:~/Desktop/study/mongoose/examples/http-server $ ./example 
24dd6 2 main.c:96:main                  Mongoose version : v7.8
24dd6 2 main.c:97:main                  Listening on     : http://0.0.0.0:8000
24dd6 2 main.c:98:main                  Web root         : [/home/pi/Desktop/study/mongoose/examples/http-server]
2a98b 2 main.c:34:cb                    GET / 200 1948

下面我们要访问https的 URL,需要启用 SSL/TLS,我们重新编译运行,使用 OpenSSL 构建,如下:

pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ make clean all SSL=OPENSSL
rm -rf example _CL* *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mbedtls
cc ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES  -DMG_ENABLE_OPENSSL=1 -lssl -lcrypto -o example main.c
./example 
HTTP/1.1 200 OK
Date: Thu, 17 Nov 2022 15:59:15 GMT
Server: Apache
Last-Modified: Wed, 05 Feb 2014 16:00:31 GMT
ETag: "286-4f1aadb3105c0"
Accept-Ranges: bytes
Content-Length: 646
Connection: close
Content-Type: text/html

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
<p>From here you can:</p>
<ul>
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
<li><a href="http://line-mode.cern.ch/www/hypertext/WWW/TheProject.html">Browse the first website using the line-mode browser simulator</a></li>
<li><a href="http://home.web.cern.ch/topics/birth-web">Learn about the birth of the web</a></li>
<li><a href="http://home.web.cern.ch/about">Learn about CERN, the physics laboratory where the web was born</a></li>
</ul>
</body></html>
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ 

重新编译后,我们访问一个https的 URL,例如访问百度的网站:

pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ ./example https://www.baidu.com/
HTTP/1.0 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Content-Length: 9508
Content-Type: text/html
Date: Thu, 17 Nov 2022 16:03:37 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BAIDUID=AB791EC8DAE58FCFEE6C4B0930839CA5:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=AB791EC8DAE58FCFEE6C4B0930839CA5; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1668701017; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=AB791EC8DAE58FCF5DADCECAE2CE29D3:FG=1; max-age=31536000; expires=Fri, 17-Nov-23 16:03:37 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Traceid: 166870101704230640747380058505918355873
Vary: Accept-Encoding
X-Frame-Options: sameorigin
X-Ua-Compatible: IE=Edge,chrome=1

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta content="always" name="referrer"><meta name="description" content="全球领先的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中文网页数据库,可以瞬间找到相关的搜索结果。"><link rel="shortcut icon" href="//www.baidu.com/favicon.ico" type="image/x-icon"><link rel="search" type="application/opensearchdescription+xml" href="//www.baidu.com/content-search.xml" title="百度搜索"><title>百度一下,你就知道</title><style type="text/css">body{margin:0;padding:0;text-align:center;background:#fff;height:100%}html{overflow-y:auto;color:#000;overflow:-moz-scrollbars;height:100%}body,input{font-size:12px;font-family:"PingFang SC",Arial,"Microsoft YaHei",sans-serif}a{text-decoration:none}a:hover{text-decoration:underline}img{border:0;-ms-interpolation-mode:bicubic}input{font-size:100%;border:0}body,form{position:relative;z-index:0}#wrapper{height:100%}#head_wrapper.s-ps-islite{padding-bottom:370px}#head_wrapper.s-ps-islite .s_form{position:relative;z-index:1}#head_wrapper.s-ps-islite .fm{position:absolute;bottom:0}#head_wrapper.s-ps-islite .s-p-top{position:absolute;bottom:40px;width:100%;height:181px}#head_wrapper.s-ps-islite #s_lg_img{position:static;margin:33px auto 0 auto;left:50%}#form{z-index:1}.s_form_wrapper{height:100%}#lh{margin:16px 0 5px;word-spacing:3px}.c-font-normal{font:13px/23px Arial,sans-serif}.c-color-t{color:#222}.c-btn,.c-btn:visited{color:#333!important}.c-btn{display:inline-block;overflow:hidden;font-family:inherit;font-weight:400;text-align:center;vertical-align:middle;outline:0;border:0;height:30px;width:80px;line-height:30px;font-size:13px;border-radius:6px;padding:0;background-color:#f5f5f6;cursor:pointer}.c-btn:hover{background-color:#315efb;color:#fff!important}a.c-btn{text-decoration:none}.c-btn-mini{height:24px;width:48px;line-height:24px}.c-btn-primary,.c-btn-primary:visited{color:#fff!important}.c-btn-primary{background-color:#4e6ef2}.c-btn-primary:hover{background-color:#315efb}a:active{color:#f60}#wrapper{position:relative;min-height:100%}#head{padding-bottom:100px;text-align:center}#wrapper{min-width:1250px;height:100%;min-height:600px}#head{position:relative;padding-bottom:0;height:100%;min-height:600px}.s_form_wrapper{height:100%}.quickdelete-wrap{position:relative}.tools{position:absolute;right:-75px}.s-isindex-wrap{position:relative}#head_wrapper.head_wrapper{width:auto}#head_wrapper{position:relative;height:40%;min-height:314px;max-height:510px;width:1000px;margin:0 auto}#head_wrapper .s-p-top{height:60%;min-height:185px;max-height:310px;position:relative;z-index:0;text-align:center}#head_wrapper input{outline:0;-webkit-appearance:none}#head_wrapper .s_btn_wr,#head_wrapper .s_ipt_wr{display:inline-block;zoom:1;background:0 0;vertical-align:top}#head_wrapper .s_ipt_wr{position:relative;width:546px}#head_wrapper .s_btn_wr{width:108px;height:44px;position:relative;z-index:2}#head_wrapper .s_ipt_wr:hover #kw{border-color:#a7aab5}#head_wrapper #kw{width:512px;height:16px;padding:12px 16px;font-size:16px;margin:0;vertical-align:top;outline:0;box-shadow:none;border-radius:10px 0 0 10px;border:2px solid #c4c7ce;background:#fff;color:#222;overflow:hidden;box-sizing:content-box}#head_wrapper #kw:focus{border-color:#4e6ef2!important;opacity:1}#head_wrapper .s_form{width:654px;height:100%;margin:0 auto;text-align:left;z-index:100}#head_wrapper .s_btn{cursor:pointer;width:108px;height:44px;line-height:45px;padding:0;background:0 0;background-color:#4e6ef2;border-radius:0 10px 10px 0;font-size:17px;color:#fff;box-shadow:none;font-weight:400;border:none;outline:0}#head_wrapper .s_btn:hover{background-color:#4662d9}#head_wrapper .s_btn:active{background-color:#4662d9}#head_wrapper .quickdelete-wrap{position:relative}#s_top_wrap{position:absolute;z-index:99;min-width:1000px;width:100%}.s-top-left{position:absolute;left:0;top:0;z-index:100;height:60px;padding-left:24px}.s-top-left .mnav{margin-right:31px;margin-top:19px;display:inline-block;position:relative}.s-top-left .mnav:hover .s-bri,.s-top-left a:hover{color:#315efb;text-decoration:none}.s-top-left .s-top-more-btn{padding-bottom:19px}.s-top-left .s-top-more-btn:hover .s-top-more{display:block}.s-top-right{position:absolute;right:0;top:0;z-index:100;height:60px;padding-right:24px}.s-top-right .s-top-right-text{margin-left:32px;margin-top:19px;display:inline-block;position:relative;vertical-align:top;cursor:pointer}.s-top-right .s-top-right-text:hover{color:#315efb}.s-top-right .s-top-login-btn{display:inline-block;margin-top:18px;margin-left:32px;font-size:13px}.s-top-right a:hover{text-decoration:none}#bottom_layer{width:100%;position:fixed;z-index:302;bottom:0;left:0;height:39px;padding-top:1px;overflow:hidden;zoom:1;margin:0;line-height:39px;background:#fff}#bottom_layer .lh{display:inline;margin-right:20px}#bottom_layer .lh:last-child{margin-left:-2px;margin-right:0}#bottom_layer .lh.activity{font-weight:700;text-decoration:underline}#bottom_layer a{font-size:12px;text-decoration:none}#bottom_layer .text-color{color:#bbb}#bottom_layer a:hover{color:#222}#bottom_layer .s-bottom-layer-content{text-align:center}</style></head><body><div id="wrapper" class="wrapper_new"><div id="head"><div id="s-top-left" class="s-top-left s-isindex-wrap"><a href="//news.baidu.com/" target="_blank" class="mnav c-font-normal c-color-t">新闻</a><a href="//www.hao123.com/" target="_blank" class="mnav c-font-normal c-color-t">hao123</a><a href="//map.baidu.com/" target="_blank" class="mnav c-font-normal c-color-t">地图</a><a href="//live.baidu.com/" target="_blank" class="mnav c-font-normal c-color-t">直播</a><a href="//haokan.baidu.com/?sfrom=baidu-top" target="_blank" class="mnav c-font-normal c-color-t">视频</a><a href="//tieba.baidu.com/" target="_blank" class="mnav c-font-normal c-color-t">贴吧</a><a href="//xueshu.baidu.com/" target="_blank" class="mnav c-font-normal c-color-t">学术</a><div class="mnav s-top-more-btn"><a href="//www.baidu.com/more/" name="tj_briicon" class="s-bri c-font-normal c-color-t" target="_blank">更多</a></div></div><div id="u1" class="s-top-right s-isindex-wrap"><a class="s-top-login-btn c-btn c-btn-primary c-btn-mini lb" style="position:relative;overflow:visible" name="tj_login" href="//www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1">登录</a></div><div id="head_wrapper" class="head_wrapper s-isindex-wrap s-ps-islite"><div class="s_form"><div class="s_form_wrapper"><div id="lg" class="s-p-top"><img hidefocus="true" id="s_lg_img" class="index-logo-src" src="//www.baidu.com/img/flexible/logo/pc/index.png" width="270" height="129" usemap="#mp"><map name="mp"><area style="outline:0" hidefocus="true" shape="rect" coords="0,0,270,129" href="//www.baidu.com/s?wd=%E7%99%BE%E5%BA%A6%E7%83%AD%E6%90%9C&amp;sa=ire_dl_gh_logo_texing&amp;rsv_dl=igh_logo_pcs" target="_blank" title="点击一下,了解更多"></map></div><a href="//www.baidu.com/" id="result_logo"></a><form id="form" name="f" action="//www.baidu.com/s" class="fm"><input type="hidden" name="ie" value="utf-8"> <input type="hidden" name="f" value="8"> <input type="hidden" name="rsv_bp" value="1"> <input type="hidden" name="rsv_idx" value="1"> <input type="hidden" name="ch" value=""> <input type="hidden" name="tn" value="baidu"> <input type="hidden" name="bar" value=""> <span class="s_ipt_wr quickdelete-wrap"><input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off"> </span><span class="s_btn_wr"><input type="submit" id="su" value="百度一下" class="bg s_btn"> </span><input type="hidden" name="rn" value=""> <input type="hidden" name="fenlei" value="256"> <input type="hidden" name="oq" value=""> <input type="hidden" name="rsv_pq" value="b9ff093e0000e419"> <input type="hidden" name="rsv_t" value="3635FYbdbC8tlWmudZmYaUnaucNe+RzTzNEGqg/JuniQU10WL5mtMQehIrU"> <input type="hidden" name="rqlang" value="cn"> <input type="hidden" name="rsv_enter" value="1"> <input type="hidden" name="rsv_dl" value="ib"></form></div></div></div><div id="bottom_layer" class="s-bottom-layer s-isindex-wrap"><div class="s-bottom-layer-content"><p class="lh"><a class="text-color" href="//home.baidu.com/" target="_blank">关于百度</a></p><p class="lh"><a class="text-color" href="//ir.baidu.com/" target="_blank">About Baidu</a></p><p class="lh"><a class="text-color" href="//www.baidu.com/duty" target="_blank">使用百度前必读</a></p><p class="lh"><a class="text-color" href="//help.baidu.com/" target="_blank">帮助中心</a></p><p class="lh"><a class="text-color" href="//www.beian.gov.cn/portal/registerSystemInfo?recordcode=11000002000001" target="_blank">京公网安备11000002000001号</a></p><p class="lh"><a class="text-color" href="//beian.miit.gov.cn/" target="_blank">京ICP证030173号</a></p><p class="lh"><span id="year" class="text-color"></span></p><p class="lh"><span class="text-color">互联网药品信息服务资格证书 (京)-经营性-2017-0020</span></p><p class="lh"><a class="text-color" href="//www.baidu.com/licence/" target="_blank">信息网络传播视听节目许可证 0110516</a></p></div></div></div></div><script type="text/javascript">var date=new Date,year=date.getFullYear();document.getElementById("year").innerText="©"+year+" Baidu "</script></body></html>
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ 

成功得到了 HTTP 响应。

如果我们没有启用 SSL/TLS,就去连接https的 URL,就会出现如下这种连接超时的错误:

pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ ./example https://www.baidu.com/
16dd6a 1 event.c:21:mg_error            1 0xffffffff Connect timeout
pi@raspberrypi:~/Desktop/study/mongoose/examples/http-client $ 

【参考资料】

examples/http-client

Documentation


本文链接:https://blog.csdn.net/u012028275/article/details/128029003

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于mongoosehttp服务是一种使用mongoose框架搭建的基于HTTP协议的服务器。Mongoose是一个轻量级的嵌入式Web服务器,它具有跨平台、高性能、低内存占用等特点,非常适合用来构建嵌入式系统或低功耗设备上的HTTP服务。 使用mongoose搭建http服务非常简单,只需几行代码即可实现一个基本的HTTP服务器。首先,我们需要引入mongoose模块,并创建一个Server对象: ``` var mongoose = require('mongoose'); var server = new mongoose.Server(); ``` 接下来,我们可以通过设置server的一些属性来配置HTTP服务器的行为,比如监听端口、设置回调函数等: ``` server.listen(8080, function() { console.log('Server is running on port 8080'); }); ``` 这样,我们就成功地创建了一个监听8080端口的HTTP服务器。当有HTTP请求到达时,服务器会触发回调函数,并对请求进行处理。我们可以在回调函数中添加业务逻辑,比如根据请求的URL返回相应的内容: ``` server.on('request', function(request, response) { // 处理HTTP请求 response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello, World!'); }); ``` 以上代码会将响应的Content-Type设置为text/plain,并返回一个简单的Hello, World!消息。 基于mongoosehttp服务还支持其他一些高级的功能,比如路由、中间件等,可以帮助我们更好地组织和处理HTTP请求。除此之外,mongoose还提供了很多其他的组件和工具,可以帮助开发者更方便地构建各种类型的Web应用。 总之,基于mongoosehttp服务是一种简单、高效的方式来构建基于HTTP协议的服务器,它提供了丰富的功能和工具,可以帮助我们快速开发各种类型的Web应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值