gpt4 book ai didi

c++ - libmicrohttpd 的 MHD_resume_connection() 无法与外部选择正常工作

转载 作者:行者123 更新时间:2023-11-30 02:29:43 25 4
gpt4 key购买 nike

在使用外部事件循环时,libmicrohttpd 中的 MHD_suspend_connection()MHD_resume_connection() 遇到了一些问题。之后我在下面写了一个小例子(没有错误处理)。我的问题是:我做错了什么?还是库中的错误?据我了解 manual 它应该可以工作.明确允许将外部选择与挂起/恢复一起使用。

问题是连接没有正确恢复。处理连接不会在调用 MHD_resume_connection() 后立即继续。在我的程序的某些版本中,它确实在收到另一个请求后继续运行。在其他版本中,根本不处理后来的请求(从未调用过access_handler())。在其中一些版本中,我在停止 libmicrohttpd 时收到了第一个请求的响应。当我启用 MHD_USE_SELECT_INTERNALY 并删除我的外部循环(让它休眠)时,一切正常。

我在 Debian (libmicrohttpd 0.9.37) 和 Arch (libmicrohttpd 0.9.50) 上测试过它。两个系统都存在问题,但行为可能略有不同。


#include <algorithm>
#include <csignal>
#include <cstring>
#include <iostream>
#include <vector>

#include <sys/select.h>

#include <microhttpd.h>

using std::cerr;
using std::cout;
using std::endl;


static volatile bool run_loop = true;
static MHD_Daemon *ctx = nullptr;
static MHD_Response *response = nullptr;
static std::vector<MHD_Connection*> susspended;

void sighandler(int)
{
run_loop = false;
}

int handle_access(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)
{
static int second_call_marker;
static int third_call_marker;
if (*con_cls == nullptr) {
cout << "New connection" << endl;
*con_cls = &second_call_marker;
return MHD_YES;
} else if (*con_cls == &second_call_marker) {
cout << "Suspending connection" << endl;
MHD_suspend_connection(connection);
susspended.push_back(connection);
*con_cls = &third_call_marker;
return MHD_YES;
} else {
cout << "Send response" << endl;
return MHD_queue_response(connection, 200, response);
}
}

void myapp()
{
std::signal(SIGINT, &sighandler);
std::signal(SIGINT, &sighandler);

ctx = MHD_start_daemon(MHD_USE_DUAL_STACK //| MHD_USE_EPOLL
| MHD_USE_SUSPEND_RESUME | MHD_USE_DEBUG,
8080, nullptr, nullptr,
&handle_access, nullptr,
MHD_OPTION_END);
response = MHD_create_response_from_buffer(4, const_cast<char*>("TEST"),
MHD_RESPMEM_PERSISTENT);

while (run_loop) {
int max;
fd_set rs, ws, es;
struct timeval tv;
struct timeval *tvp;

max = 0;
FD_ZERO(&rs);
FD_ZERO(&ws);
FD_ZERO(&es);

cout << "Wait for IO activity" << endl;
MHD_UNSIGNED_LONG_LONG mhd_timeout;
MHD_get_fdset(ctx, &rs, &ws, &es, &max);
if (MHD_get_timeout(ctx, &mhd_timeout) == MHD_YES) {
//tv.tv_sec = std::min(mhd_timeout / 1000, 1ull);
tv.tv_sec = mhd_timeout / 1000;
tv.tv_usec = (mhd_timeout % 1000) * 1000;
tvp = &tv;
} else {
//tv.tv_sec = 2;
//tv.tv_usec = 0;
//tvp = &tv;
tvp = nullptr;
}

if (select(max + 1, &rs, &ws, &es, tvp) < 0 && errno != EINTR)
throw "select() failed";

cout << "Handle IO activity" << endl;
if (MHD_run_from_select(ctx, &rs, &ws, &es) != MHD_YES)
throw "MHD_run_from_select() failed";

for (MHD_Connection *connection : susspended) {
cout << "Resume connection" << endl;
MHD_resume_connection(connection);
}
susspended.clear();
}

cout << "Stop server" << endl;
MHD_stop_daemon(ctx);
}

int main(int argc, char *argv[])
{
try {
myapp();
} catch (const char *str) {
cerr << "Error: " << str << endl;
cerr << "Errno: " << errno << " (" << strerror(errno) << ")" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

最佳答案

我已经在 Windows 上编译并运行了您的示例,并且在 0.9.51 中看到了相同的行为。

这不是 microhttpd 中的错误。问题是您在排队响应之前恢复连接。您拥有的唯一创建响应的代码依赖于连接上的更多事件,因此它是一个陷阱 22。

MHD_suspend_connection/MHD_resume_connection 的要点是在长时间运行的工作正在进行时不阻止新连接。因此,通常在挂起连接后,您需要在另一个线程上启动该工作以继续,同时保持监听套接字。当该线程将响应排队后,它可以恢复连接,事件循环将知道它已准备好发送回客户端。

我不确定您的其他设计要求,但您可能不需要实现外部选择。也就是说,suspend/resume 不需要它(例如,我在 MHD_USE_SELECT_INTERNALY 中使用 suspend/resume 就好了)。

关于c++ - libmicrohttpd 的 MHD_resume_connection() 无法与外部选择正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39312592/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com