libmicrohttpd的MHD_resume_connection()在外部选择时不能正常工作
MHD_resume_connection() of libmicrohttpd not working properly with external select
我在libmicrohttpd中使用外部事件循环时遇到了MHD_suspend_connection()
和MHD_resume_connection()
的一些问题。之后,我在下面写了一个小例子(没有错误处理)。我的问题是:我做错了什么?还是库中的bug ?就我对手册的理解,它应该可以工作。明确允许使用带有suspend/resume的外部选择。
问题是连接没有正确恢复。调用MHD_resume_connection()
后连接处理没有继续。在我的程序的某些版本中,它确实在另一个请求到来后继续运行。在其他版本中,以后的请求根本不处理(从未调用access_handler()
)。在其中一些版本中,我在停止libmicrohttpd时得到了第一个请求的响应。当我启用MHD_USE_SELECT_INTERNALLY
并删除外部循环(让它休眠)时,一切都工作了。
我在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上编译并运行了您的示例,并且看到了与w0.9.51相同的行为。
这不是microhttpd中的bug。问题是,在对其排队响应之前,您正在恢复连接。创建响应的唯一代码依赖于连接上的更多活动,所以这是一个第22条军规。
MHD_suspend_connection
/MHD_resume_connection
的要点是在长时间运行的工作正在进行时不阻塞新的连接。因此,通常在挂起连接之后,您需要启动另一个线程上的工作,以便在维护侦听套接字的同时继续工作。当该线程将响应排队时,它可以恢复连接,事件循环将知道它已准备好发送回客户端。
我不确定您的其他设计要求,但您可能不需要实现外部选择。也就是说,暂停/恢复不需要它(我使用暂停/恢复很好与MHD_USE_SELECT_INTERNALLY
,例如)。
我不知道有没有提到。但是你有一个多线程bug,也许还有"意图bug"。作为库,可以使用线程,也可以不使用线程,这取决于其他因素。您可以通过从函数中打印线程id来查看是否正在使用线程。但是,你的answerToConnection函数,设置你的向量(没有互斥保护),然后你立即查看它,并从另一个线程重试潜在。这违背了挂起/重试的意图/目的,因为挂起实际上是用于花费"很长时间"的事情。问题是,你不拥有调用代码,所以,你不知道它什么时候完全完成。但是,您可以使用timeval来延长重试时间,这样您就不会重试得太快。至少为tv_usec +1。需要注意的是,您正在使用来自两个或多个线程的向量,没有互斥锁保护。
- 为什么 HeapFree() 不能正常工作?
- C++自定义删除运算符不能正常工作?
- 为什么使用 exec() 重新启动程序不能正常工作?
- C++:返回本地对象,但不能正常工作
- 为什么带lcov的codecov在Travis上不能正常工作,而在我当地的Linux Mint上却不能正常工作
- 为什么sizeof函数在这里不能正常工作
- 为什么这个base64解码/编码功能不能正常工作?
- 如果 (QString.contains()) 不能正常工作,请使用 temp bool 变量进行更正
- 为什么我的数组代码的反向不能正常工作
- 我正在制作一个刽子手游戏,我需要帮助弄清楚为什么它不能完全工作
- 为什么它不能正常工作,Numeric_limits
- C++:printf 和 wprintf 不能一起工作 (Linux)
- 为什么这个模板类不能正常工作?
- 气泡排序:为什么它不能正常工作?
- 为什么这段代码不能正常工作???我的意思是,为什么当我运行它时它没有显示任何内容?
- 使用复制构造函数C++不能正常工作
- typeinfo name() 和 endl 在 Windows 和 mingw 中不能一起工作
- 虽然循环不能正常工作,但 C++
- 为什么这个C++代码(来自C++入门5th 3.4.2)不能正常工作?
- boost和Curl不能一起工作