GNU libmicrohttpd 库教程外文翻译资料

 2023-03-14 07:03

GNU libmicrohttpd 库教程

关键词:HTTP,服务器,请求,守护进程

2 Hello browser的例子

HTTP 服务器最基本的任务是向连接到它的任何客户机传递静态文本消息。考虑到这也很容易实现,这是一个很好的问题作为本章的开头。

目前,客户端请求的特定 URI 对将要返回的消息没有任何影响。此外,服务器应该在消息发送之后结束连接,这样客户端就会知道没有其他消息需要等待了。

C 语言程序 hellobrowser.c (可以在示例一节中找到)就是这样做的。如果您非常渴望运行结果,那么您可以立即编译并启动它,但是您最好自己键入这些代码行,因为它们将被详细讨论和解释。

之后的必要包括和定义的端口,我们的服务器应该开始监听

#include lt;sys/types.hgt;

#include lt;sys/select.hgt;

#include lt;sys/socket.hgt;

#include lt;microhttpd.hgt;

#define PORT 8888

当 HTTP 请求到达时,我们的服务器必须执行所需的行为。我们已经默许它不应该关心请求的具体细节,例如谁在请求什么。服务器只会对每个请求做出相同的小 HTML 页面响应。

我们现在要编写的函数将由 GNU libmicrohttpd 在每次有合适的请求进入时调用。虽然这个回调函数的名称是任意的,但是它的参数列表必须遵循特定的结构。所以,请暂时忽略这些参数,它们将在需要的时候被解释。我们只能使用其中的一个,结构 MHD_Connection *connection,为了达到我们目前想要达到的最简单的功能。

该参数由 libmicrohttpd 守护进程设置,并保存了将调用与特定连接关联的必要信息。请记住,服务器可能必须满足数百个并发连接,我们必须确保将正确的数据发送到指定的客户机。因此,如果我们要求守护进程发送响应回复,则此变量是引用特定连接的一种方法。

说到响应回复,它被定义为函数头后面的字符串

int answer_to_connection (void *cls, struct MHD_Connection *connection,

const char *url,

const char *method, const char *version,

const char *upload_data,

size_t *upload_data_size, void **con_cls)

{

const char *page = 'lt;htmlgt;lt;bodygt;Hello, browser!lt;/bodygt;lt;/htmlgt;';

HTTP 是一个相当严格的协议,如果我们只是按原样发送应答字符串,客户机肯定会认为它是“不适当的”。相反,它必须用存储在所谓的请求头和请求尾的附加信息进行封装。这个领域的大部分工作都是由调用库为我们完成的——我们只需要调用就行了。我们的应答字符串打包在必要的层将被称为“响应”。为了获得这样的响应,我们将数据(应答字符串)及其大小传递给MHD_create_response_from_buffer 函数。最后两个参数基本上告诉 MHD,我们不希望它在发送消息数据时为我们处理消息数据,也不需要进行内部复制,因为常量字符串无论如何都不会改变。

struct MHD_Response *response;

int ret;

response = MHD_create_response_from_buffer (strlen (page),

(void*) page, MHD_RESPMEM_PERSISTENT);

现在响应已经被修改,可以准备发送并排队等待发送。这是通过将其传递给另一个 GNU libmicrohttpd 函数来完成的。由于我们所有的工作都在一个函数的范围内完成,接收者无疑就是与局部变量连接相关联的那个,因此这个变量被赋予队列函数。每个 HTTP 响应都附带一个状态码,这里的状态码是“ OK”,这样客户机就知道这个响应是他的请求的预期结果,而不是由于某些错误或故障。

最后,数据包被销毁,队列的返回值返回,此时已经被设置为 MHD YES 或者是 MHD NO,以示成功或失败。

ret = MHD_queue_response (connection, MHD_HTTP_OK, response);

MHD_destroy_response (response);

return ret;

}

在实现了服务器的主要任务之后,我们可以启动实际的服务器守护进程,它将在 PORT 上侦听连接。这是在主函数中完成的。

int main ()

{

struct MHD_Daemon *daemon;

daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,

amp;answer_to_connection, NULL, MHD_OPTION_END);

if (NULL == daemon) return 1;

第一个参数是三种可能的操作模式之一。在这里,我们希望守护进程在单独的线程中运行,并在同一个线程中管理所有传入的连接。这意味着,当为一个连接生成响应时,其他连接将被搁置。在这个示例中,如果已经知道响应,因此请求将被迅速处理,这样就不会造成任何问题。

我们将允许所有客户机连接,而不管它们的名称或位置,因此我们不检查它们的连接,并将第三和第四个参数设置为 NULL。

参数5是我们希望在建立新连接时调用的函数的地址。我们的 answer_to_connection 最清楚客户机需要什么,不需要额外的信息(可以通过下一个参数传递) ,因此下一个(第六个)参数为 NULL。同样,我们不需要向守护进程传递额外的选项,因此我们只需将 MHD_OPTION_END 写为最后一个参数。

由于服务器守护进程在它自己的线程中的后台运行,我们的主函数中的执行流将在调用之后立即继续。因此,我们必须延迟主线程中的执行流,否则程序将提前终止。通过等待按下回车键,我们让它以一种处理时友好的方式暂停。最后,我们停止这个守护进程,以便它可以执行清理任务。

getchar ();

MHD_stop_daemon (daemon);

return 0;

}

第一个例子现在已经完成。

现在编译它,用以下的命令:

cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES

-L$PATH_TO_LIBMHD_LIBS -lmicrohttpd

相应地设置两条路径并运行它。

现在打开你最喜欢的互联网浏览器,进入 http://localhost:8888/地址,前提是8888是你选择的端口。如果一切正常,浏览器将显示从我们建立的微小服务器获得的静态 HTML 页面的消息。

备注

为了使第一个示例尽可能小,我们采取了一些极端的捷径,现在将讨论这些捷径。

首先,对于客户端可以发送的请求类型没有区别。我们暗示客户端发送一个 GET 请求,这意味着他实际上要求一些数据。虽然不打算接受 POST 请求,一个好的服务器至少应该认识到这个请求不构成合法请求,并用错误代码回答。通过检查参数方法是否等于字符串“ GET”,如果不等于则返回一个MHD_NO,这一点可以很容易实现。

其次,上述在回调函数第一次调用时将响应排队的做法带来了一些限制。这是因为如果响应在第一次迭代中排队,则不会接收消息体的内容。此外,在传输响应后,连接将立即关闭。这通常不是您想要的,因为它禁用了HTTP管道。正确的方法是,除非出现错误,否则不要在第一次回调时将消息排队。回调函数的void**参数提供用于存储有关连接历史记录的信息的位置;对于第一个调用,指针将指向NULL。区分第一次调用与其他调用的一种简单方法是检查指针是否为 NULL,并在第一次调用期间将其设置为非 NULL 值。

这两个问题都可以在 MHD 包的 src/examples 目录中的官方示例minimal_example.c 中找到。这个程序的源代码现在应该看起来非常熟悉,并且很容易理解。

对于我们的上述示例,我们从内存中的静态(持久)缓冲区创建响应,从而将MHD_RESPMEM_PERSISTENT传递给响应构造函数。在通常情况下,响应不会在排队后立即传送。例如,系统上可能有其他数据需要以更高的优先级发送。尽管如此,队列函数将成功地返回,这就产生了一个问题,即我们所指向的数据在发送时可能已经失效。但这在上述的示例中不会存在该问题,因为我们可以预期页面字符串(在这里是一个常量字符串)是静态的。这意味着只要程序运行,它就会一直存在并且不会改变。对于动态数据,可以选择让MHD释放不再需要的内存页指向自身(通过传递MHD_RESPMEM_MUST_free),或者让库制作和管理它自己的副本(通过传递MHD_RESPMEM_MUST_copy)。当然,最后一种方法消耗时空时最大的。

练习

  • 当服务器运行时,使用如telnet或netcat的程序,请尝试自己形成一个有效的 HTTP 1.1请求。

GET /dontcare HTTP/1.1

Host: itsme

lt;entergt;

看看服务器会返回给你什么。

  • 此外,尝试其他请求,如 POST,看看我们的服务器如何不介意post请求和并理解为什么。在MHD的内置功能介入并发送更改后的响应之前,能多大程度上修改请求?这需要您确保阅读了 RFC 中的状态代码。
  • 将选项MHD_USE_PEDANTIC_CHECKS添加到main中守护进程的启动函数中。请注意本手册中描述的参数列表的特殊格式。服务器现在对您的输入有多宽容?
  • 让 main 函数以一个字符串作为第一个命令行参数,并将 argv [1]作为第六个参数传递给 MHD_start_daemon 函数。这个字符串的地址将通过 默认变量传递给回调函数。当服务器启动时,用适当的HTML标记修饰命令行中给出的文本,并将其作为响应发送,而不是以前的静态字符串。
  • 要求: 编写一个单独的函数返回一个包含一些有用信息的字符串,例如时间。将函数的地址作为第六个参数

    剩余内容已隐藏,支付完成后下载完整资料


    英语原文共 1 页,剩余内容已隐藏,支付完成后下载完整资料


    资料编号:[596155],资料为PDF文档或Word文档,PDF文档可免费转换为Word

您需要先支付 30元 才能查看全部内容!立即支付

课题毕业论文、文献综述、任务书、外文翻译、程序设计、图纸设计等资料可联系客服协助查找。