WinHttp编写HTTP服务器示例代码
这是微软提供的示例程序,原文地址在此https://msdn.microsoft.com/en-us/library/windows/desktop/aa364640(v=vs.85).aspx
HTTP Server示例程序
以下示例应用程序展示如何使用HTTP Server API
处理HTTP请求任务。第一个示例中包含的precomp.h
文件包含示例所需的所有头文件,如下:
#ifndef UNICODE #define UNICODE #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <http.h> #include <stdio.h> #pragma comment(lib, "httpapi.lib")
Main and Preliminaries(main和准备工作)#
#include "precomp.h" // // Macros.初始化HTTP响应体宏 // #define INITIALIZE_HTTP_RESPONSE( resp, status, reason ) \ do \ { \ RtlZeroMemory( (resp), sizeof(*(resp)) ); \ (resp)->StatusCode = (status); \ (resp)->pReason = (reason); \ (resp)->ReasonLength = (USHORT) strlen(reason); \ } while (FALSE) #define ADD_KNOWN_HEADER(Response, HeaderId, RawValue) \ do \ { \ (Response).Headers.KnownHeaders[(HeaderId)].pRawValue = \ (RawValue);\ (Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \ (USHORT) strlen(RawValue); \ } while(FALSE) #define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb)) #define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr)) // // Prototypes.原型 // DWORD DoReceiveRequests(HANDLE hReqQueue); DWORD SendHttpResponse( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest, IN USHORT StatusCode, IN PSTR pReason, IN PSTR pEntity ); DWORD SendHttpPostResponse( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest ); /*******************************************************************++ 函数说明: main函数 参数: argc - 命令行参数个数. argv - 命令行参数. 返回值: Success/Failure --*******************************************************************/ int __cdecl wmain( int argc, wchar_t * argv[] ) { ULONG retCode; HANDLE hReqQueue = NULL; int UrlAdded = 0; HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1; if (argc < 2) { wprintf(L"%ws: <Url1> [Url2] ... \n", argv[0]); return -1; }
初始化HTTP Service#
// // 初始化HTTP Server APIs // retCode = HttpInitialize( HttpApiVersion, HTTP_INITIALIZE_SERVER, // Flags NULL // Reserved ); if (retCode != NO_ERROR) { wprintf(L"HttpInitialize failed with %lu \n", retCode); return retCode; } // // 创建请求队列句柄 // retCode = HttpCreateHttpHandle( &hReqQueue, // Req Queue 0 // Reserved ); if (retCode != NO_ERROR) { wprintf(L"HttpCreateHttpHandle failed with %lu \n", retCode); goto CleanUp; }
注册URLs进行监听#
// // 命令行参数指定要监听的URI。为每个URI调用HttpAddUrl。 // // URI是一个完全合格的URI,必须包含终止字符(/) // for (int i = 1; i < argc; i++) { wprintf(L"listening for requests on the following url: %s\n", argv[i]); retCode = HttpAddUrl( hReqQueue, // Req Queue argv[i], // Fully qualified URL NULL // Reserved ); if (retCode != NO_ERROR) { wprintf(L"HttpAddUrl failed with %lu \n", retCode); goto CleanUp; } else { // // Track the currently added URLs. // UrlAdded ++; } }
调用程序以接收请求#
DoReceiveRequests(hReqQueue);
清理HTTP Server API#
CleanUp: // // 对所有添加的URI调用HttpRemoveUrl. // for(int i=1; i<=UrlAdded; i++) { HttpRemoveUrl( hReqQueue, // Req Queue argv[i] // Fully qualified URL ); } // // 关闭请求队列句柄. // if(hReqQueue) { CloseHandle(hReqQueue); } // // 调用HttpTerminate. // HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); return retCode;
接收请求#
/*******************************************************************++ 函数说明: 他的功能是接收一个请求。 该函数调用相应的函数来处理响应。 参数: hReqQueue - 请求队列句柄 返回值: Success/Failure. --*******************************************************************/ DWORD DoReceiveRequests( IN HANDLE hReqQueue ) { ULONG result; HTTP_REQUEST_ID requestId; DWORD bytesRead; PHTTP_REQUEST pRequest; PCHAR pRequestBuffer; ULONG RequestBufferLength; // // 分配一个2 KB缓冲区。 这个大小应该适用于大多数请求。 如果需要, // 可以增加缓冲区大小。HTTP_REQUEST结构也需要空间。 // RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pRequest = (PHTTP_REQUEST)pRequestBuffer; // // 等待一个新请求. 标记为一个NULL请求ID // HTTP_SET_NULL_ID( &requestId ); for(;;) { RtlZeroMemory(pRequest, RequestBufferLength); result = HttpReceiveHttpRequest( hReqQueue, // Req Queue requestId, // Req ID 0, // Flags pRequest, // HTTP request buffer RequestBufferLength,// req buffer length &bytesRead, // bytes received NULL // LPOVERLAPPED );
处理HTTP请求#
if(NO_ERROR == result) { // // Worked! // switch(pRequest->Verb) { /* GET 请求处理 */ case HttpVerbGET: wprintf(L"Got a GET request for %ws \n", pRequest->CookedUrl.pFullUrl); result = SendHttpResponse( hReqQueue, pRequest, 200, "OK", "Hey! You hit the server \r\n" ); break; /* POST 请求处理 */ case HttpVerbPOST: wprintf(L"Got a POST request for %ws \n", pRequest->CookedUrl.pFullUrl); result= SendHttpPostResponse(hReqQueue, pRequest); break; default: wprintf(L"Got a unknown request for %ws \n", pRequest->CookedUrl.pFullUrl); result = SendHttpResponse( hReqQueue, pRequest, 503, "Not Implemented", NULL ); break; } if(result != NO_ERROR) { break; } // // 重置请求ID用于处理下一个请求. // HTTP_SET_NULL_ID( &requestId ); } else if(result == ERROR_MORE_DATA) { // // 输入缓冲区太小,无法容纳请求标头。增加缓冲区大小,再次调用API。 // // 再次调用API时,通过传递RequestID来处理失败的请求。 // // 该RequestID从旧缓冲区读取。 // requestId = pRequest->RequestId; // // 释放旧的缓冲区并分配一个新的缓冲区。 // RequestBufferLength = bytesRead; FREE_MEM( pRequestBuffer ); pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; break; } pRequest = (PHTTP_REQUEST)pRequestBuffer; } else if(ERROR_CONNECTION_INVALID == result && !HTTP_IS_NULL_ID(&requestId)) { // 当尝试使用更多缓冲区来处理请求时,TCP连接被对方破坏 // 继续下一个请求。 HTTP_SET_NULL_ID( &requestId ); } else { break; } } if(pRequestBuffer) { FREE_MEM( pRequestBuffer ); } return result; }
发送一个HTTP响应#
/*******************************************************************++ 函数说明: 这个函数用于发送一个HTTP响应 参数: hReqQueue - 请求队列句柄 pRequest - 解析出的HTTP请求 StatusCode - Response状态码 pReason - Response原因短语 pEntityString - Response实体主体 返回值: Success/Failure. --*******************************************************************/ DWORD SendHttpResponse( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest, IN USHORT StatusCode, IN PSTR pReason, IN PSTR pEntityString ) { HTTP_RESPONSE response; HTTP_DATA_CHUNK dataChunk; DWORD result; DWORD bytesSent; // // 初始化HTTP response结构体 // INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason); // // 添加一个known header. // ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html"); if(pEntityString) { // // 添加一个entity chunk. // dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.pBuffer = pEntityString; dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString); response.EntityChunkCount = 1; response.pEntityChunks = &dataChunk; } // // 因为entity body在一个调用中发送,所以不需要指定Content-Length。 // result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID 0, // Flags &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent (OPTIONAL) NULL, // pReserved2 (must be NULL) 0, // Reserved3 (must be 0) NULL, // LPOVERLAPPED(OPTIONAL) NULL // pReserved4 (must be NULL) ); if(result != NO_ERROR) { wprintf(L"HttpSendHttpResponse failed with %lu \n", result); } return result; }
发送一个HTTP POST响应#
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295")) /*******************************************************************++ 函数说明: 这个函数在读取entity body后发送HTTP响应 参数: hReqQueue - 请求队列句柄 pRequest - 解析出的HTTP request. 返回值: Success/Failure. --*******************************************************************/ DWORD SendHttpPostResponse( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest ) { HTTP_RESPONSE response; DWORD result; DWORD bytesSent; PUCHAR pEntityBuffer; ULONG EntityBufferLength; ULONG BytesRead; ULONG TempFileBytesWritten; HANDLE hTempFile; TCHAR szTempName[MAX_PATH + 1]; CHAR szContentLength[MAX_ULONG_STR]; HTTP_DATA_CHUNK dataChunk; ULONG TotalBytesRead = 0; BytesRead = 0; hTempFile = INVALID_HANDLE_VALUE; // // 为实体缓冲区分配空间。 缓冲区可按需增加。 // EntityBufferLength = 2048; pEntityBuffer = (PUCHAR) ALLOC_MEM( EntityBufferLength ); if (pEntityBuffer == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; wprintf(L"Insufficient resources \n"); goto Done; } // // 初始化HTTP response结构体. // INITIALIZE_HTTP_RESPONSE(&response, 200, "OK"); // // 对于POST,从客户端回显实体 // // 注意: 如果HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY标识通过HttpReceiveHttpRequest() // 传递,则entity将是HTTP_REQUEST的一部分(使用pEntityChunks字段).因为此标识 // 未被传递,则entity不在HTTP_REQUEST中. // if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS) { // 实体主体通过多个调用发送. 收集这些在一个文件并回发.创建一个临时文件 // if(GetTempFileName( L".", L"New", 0, szTempName ) == 0) { result = GetLastError(); wprintf(L"GetTempFileName failed with %lu \n", result); goto Done; } hTempFile = CreateFile( szTempName, GENERIC_READ | GENERIC_WRITE, 0, // Do not share. NULL, // No security descriptor. CREATE_ALWAYS, // Overrwrite existing. FILE_ATTRIBUTE_NORMAL, // Normal file. NULL ); if(hTempFile == INVALID_HANDLE_VALUE) { result = GetLastError(); wprintf(L"Cannot create temporary file. Error %lu \n", result); goto Done; } do { // // 从请求中读取entity chunk. // BytesRead = 0; result = HttpReceiveRequestEntityBody( hReqQueue, pRequest->RequestId, 0, pEntityBuffer, EntityBufferLength, &BytesRead, NULL ); switch(result) { case NO_ERROR: if(BytesRead != 0) { TotalBytesRead += BytesRead; WriteFile( hTempFile, pEntityBuffer, BytesRead, &TempFileBytesWritten, NULL ); } break; case ERROR_HANDLE_EOF: // // The last request entity body has been read. // Send back a response. // // To illustrate entity sends via // HttpSendResponseEntityBody, the response will // be sent over multiple calls. To do this, // pass the HTTP_SEND_RESPONSE_FLAG_MORE_DATA // flag. if(BytesRead != 0) { TotalBytesRead += BytesRead; WriteFile( hTempFile, pEntityBuffer, BytesRead, &TempFileBytesWritten, NULL ); } // // Because the response is sent over multiple // API calls, add a content-length. // // Alternatively, the response could have been // sent using chunked transfer encoding, by // passimg "Transfer-Encoding: Chunked". // // NOTE: Because the TotalBytesread in a ULONG // are accumulated, this will not work // for entity bodies larger than 4 GB. // For support of large entity bodies, // use a ULONGLONG. // sprintf_s(szContentLength, MAX_ULONG_STR, "%lu", TotalBytesRead); ADD_KNOWN_HEADER( response, HttpHeaderContentLength, szContentLength ); result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID HTTP_SEND_RESPONSE_FLAG_MORE_DATA, &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent-optional NULL, // pReserved2 0, // Reserved3 NULL, // LPOVERLAPPED NULL // pReserved4 ); if(result != NO_ERROR) { wprintf( L"HttpSendHttpResponse failed with %lu \n", result ); goto Done; } // // Send entity body from a file handle. // dataChunk.DataChunkType = HttpDataChunkFromFileHandle; dataChunk.FromFileHandle. ByteRange.StartingOffset.QuadPart = 0; dataChunk.FromFileHandle. ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF; dataChunk.FromFileHandle.FileHandle = hTempFile; result = HttpSendResponseEntityBody( hReqQueue, pRequest->RequestId, 0, // This is the last send. 1, // Entity Chunk Count. &dataChunk, NULL, NULL, 0, NULL, NULL ); if(result != NO_ERROR) { wprintf( L"HttpSendResponseEntityBody failed %lu\n", result ); } goto Done; break; default: wprintf( L"HttpReceiveRequestEntityBody failed with %lu \n", result); goto Done; } } while(TRUE); } else { // 此请求没有实体主体。 // result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID 0, &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent (optional) NULL, // pReserved2 0, // Reserved3 NULL, // LPOVERLAPPED NULL // pReserved4 ); if(result != NO_ERROR) { wprintf(L"HttpSendHttpResponse failed with %lu \n", result); } } Done: if(pEntityBuffer) { FREE_MEM(pEntityBuffer); } if(INVALID_HANDLE_VALUE != hTempFile) { CloseHandle(hTempFile); DeleteFile(szTempName); } return result; }
分类:
C/C++
, windows编程开发
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理