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 等。