libmicrohttpd 是一个小型的 C 库,用于在项目中嵌入 HTTP 服务器功能。它设计简单、轻量级,适合需要 HTTP 接口但不想要大型 Web 服务器开销的应用程序。
安装 libmicrohttpd
Linux 系统
在基于 Debian/Ubuntu 的系统上:
bash
sudo apt-get install libmicrohttpd-dev
在基于 RHEL/CentOS 的系统上:
bash
sudo yum install libmicrohttpd-devel
macOS 系统
bash
brew install libmicrohttpd
从源码编译
-
下载源码:https://ftp.gnu.org/gnu/libmicrohttpd/
-
解压并进入目录
-
编译安装:
bash
./configure
make
sudo make install
基本使用示例
最简单的 HTTP 服务器
c
#include <microhttpd.h>
#include <stdio.h>
#include <string.h>#define PORT 8888static enum MHD_Result 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 = "<html><body>Hello, browser!</body></html>";struct MHD_Response *response;enum MHD_Result ret;response = MHD_create_response_from_buffer(strlen(page),(void*)page, MHD_RESPMEM_PERSISTENT);ret = MHD_queue_response(connection, MHD_HTTP_OK, response);MHD_destroy_response(response);return ret;
}int main()
{struct MHD_Daemon *daemon;daemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, PORT,NULL, NULL, &answer_to_connection, NULL,MHD_OPTION_END);if (NULL == daemon) {return 1;}getchar();MHD_stop_daemon(daemon);return 0;
}
编译并运行:
bash
gcc simple_server.c -o server -lmicrohttpd
./server
然后访问 http://localhost:8888
主要功能特性
-
支持 HTTP 1.1
-
多种线程模型:
-
单线程
-
多线程(线程池)
-
每个连接一个线程
-
-
支持 HTTPS(需要 GnuTLS 或类似库)
-
IPv6 支持
-
基本认证和摘要认证
-
可处理 POST 数据
-
非阻塞模式
处理 POST 请求示例
c
#include <microhttpd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define PORT 8888struct connection_info {char *data;size_t size;
};static enum MHD_Result post_iterator(void *cls, enum MHD_ValueKind kind,const char *key, const char *filename,const char *content_type,const char *transfer_encoding,const char *data, uint64_t off,size_t size)
{struct connection_info *con_info = cls;if (0 == size) return MHD_YES;if (NULL == con_info->data) {con_info->data = malloc(size + 1);if (NULL == con_info->data) return MHD_NO;con_info->size = size;memcpy(con_info->data, data, size);con_info->data[size] = '\0';} else {char *new_data = realloc(con_info->data, con_info->size + size + 1);if (NULL == new_data) return MHD_NO;con_info->data = new_data;memcpy(con_info->data + con_info->size, data, size);con_info->size += size;con_info->data[con_info->size] = '\0';}return MHD_YES;
}static void request_completed(void *cls, struct MHD_Connection *connection,void **con_cls, enum MHD_RequestTerminationCode toe)
{struct connection_info *con_info = *con_cls;if (NULL == con_info) return;if (NULL != con_info->data) free(con_info->data);free(con_info);*con_cls = NULL;
}static enum MHD_Result 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)
{if (NULL == *con_cls) {struct connection_info *con_info;con_info = malloc(sizeof(struct connection_info));if (NULL == con_info) return MHD_NO;con_info->data = NULL;con_info->size = 0;*con_cls = con_info;return MHD_YES;}if (0 != strcmp(method, "POST")) {const char *page = "<html><body>This server only accepts POST requests</body></html>";struct MHD_Response *response;enum MHD_Result ret;response = MHD_create_response_from_buffer(strlen(page),(void*)page, MHD_RESPMEM_PERSISTENT);ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, response);MHD_destroy_response(response);return ret;}if (*upload_data_size != 0) {struct connection_info *con_info = *con_cls;MHD_post_process(con_info->post_processor, upload_data, *upload_data_size);*upload_data_size = 0;return MHD_YES;} else {struct connection_info *con_info = *con_cls;const char *page;struct MHD_Response *response;enum MHD_Result ret;if (NULL == con_info->data) {page = "<html><body>No data received</body></html>";} else {char *reply = malloc(100 + con_info->size);sprintf(reply, "<html><body>You posted: %s</body></html>", con_info->data);page = reply;}response = MHD_create_response_from_buffer(strlen(page),(void*)page, MHD_RESPMEM_MUST_FREE);ret = MHD_queue_response(connection, MHD_HTTP_OK, response);MHD_destroy_response(response);return ret;}
}int main()
{struct MHD_Daemon *daemon;daemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, PORT,NULL, NULL, &answer_to_connection, NULL,MHD_OPTION_NOTIFY_COMPLETED, &request_completed, NULL,MHD_OPTION_END);if (NULL == daemon) {return 1;}getchar();MHD_stop_daemon(daemon);return 0;
}
进阶主题
-
HTTPS 支持:
-
需要链接 GnuTLS 库
-
使用
MHD_USE_SSL
标志 -
提供证书和密钥文件
-
-
多线程模式:
-
MHD_USE_THREAD_PER_CONNECTION
- 每个连接一个线程 -
MHD_USE_INTERNAL_POLLING_THREAD
- 内部轮询线程 -
MHD_USE_SELECT_INTERNALLY
- 使用 select() 进行内部轮询
-
-
性能优化:
-
使用连接池
-
合理设置线程数
-
启用 TCP 快速打开
-
资源
-
官方文档
-
API 参考手册
-
GitHub 仓库
这个入门指南涵盖了 libmicrohttpd 的基本用法。根据你的需求,可以进一步探索更高级的功能如 WebSocket 支持、HTTP/2 等。