MFC调用winhttp实现简易的HTTP服务器程序

    技术2022-07-11  106

    Windows提供的winhttp函数库用来快速实现HTTP协议的应用程序,包括客户端和服务器端。

    服务器开发一般流程是这样的:

    1.HttpInitialize 初始化WinHTTP函数库;

    2.HttpCreateHttpHandle 创建一个HTTP队列句柄,用来接收HTTP请求;

    3.HttpAddUrl 绑定要监听的URL,写为http://*:80/表示所有网卡80端口的HTTP请求都处理,其中的*号可以改为IP地址;

    4.创建一个线程用来处理HTTP请求队列;

    5.HttpReceiveHttpRequest 在线程中调用此函数,接收HTTP请求,在返回的PHTTP_REQUEST结构中有我们想要的各种数据;

    6.HttpReceiveRequestEntityBody 这个函数用来接收HTTP请求的BODY部分,如果数据很长需要多次调用;

    7.HttpSendHttpResponse 这个函数返回响应请求的数据,可以自行设置Header和Body;

    8.要结束HTTP服务器,调用CloseHandle关闭HttpCreateHttpHandle返回的队列句柄;再调用HttpTerminate清理WinHTTP函数库;


    下面的代码实现了一个最简单的HTTP服务器程序,仅供参考。

    用法:

    1. 创建IHttpServiceListener的派生类,实现该虚基类的OnRecvRequest函数,该函数是用于收到客户端请求时的回调函数。在回调函数中处理请求数据,如果要读取客户端POST的数据,则调用inst->RecvRequestBody;最后调用inst->SendRequestResp返回响应数据。

    2.创建CHttpService类的实例,调用Create函数,提供监听端口和回调接口。


    头文件:HttpService.h

    #pragma once #include <http.h> class CHttpService; class IHttpServiceListener { public: virtual BOOL OnRecvRequest(CHttpService *inst, PHTTP_REQUEST request)=0; }; class CHttpService { public: CHttpService(); BOOL Create(INT port, IHttpServiceListener *listener); BOOL Delete(void); BOOL SendRequestResp(HTTP_REQUEST_ID req_id, INT status, LPCSTR reason, LPCSTR type, HANDLE file, LPVOID mem, DWORD mem_size); BOOL RecvRequestBody(HTTP_REQUEST_ID req_id, HANDLE file, char *mem, DWORD mem_size); BOOL DoReceiveRequests(); BOOL GetRemoteAddr(PHTTP_REQUEST request, CString &text); public: static UINT AFX_CDECL RecvRequestThread(LPVOID param); private: HANDLE m_req_queue; LPBYTE m_req_buffer; UINT m_req_buffer_size; CWinThread *m_thread; IHttpServiceListener *m_listener; };

     源文件:HttpService.cpp

    #include "StdAfx.h" #include "HttpService.h" #pragma comment(lib, "httpapi.lib") CHttpService::CHttpService() { m_thread = NULL; m_req_queue = NULL; m_req_buffer = NULL; m_req_buffer_size = 4096; } BOOL CHttpService::Create(INT port, IHttpServiceListener *listener) { ULONG ret; WCHAR url[1024]; HTTPAPI_VERSION version = HTTP_VERSION_1_0; swprintf(url, 1024, L"http://*:%d/", port); ret = HttpInitialize(version,HTTP_INITIALIZE_SERVER,NULL); if(ret != NO_ERROR) { TRACE("HttpInitialize error(%u)!\r\n", ret); return FALSE; } ret = HttpCreateHttpHandle(&m_req_queue, 0); if(ret != NO_ERROR) { TRACE("HttpCreateHttpHandle error(%u)!\n", ret); HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); return FALSE; } ret = HttpAddUrl(m_req_queue, url, NULL); if(ret != NO_ERROR) { TRACE("HttpAddUrl error(%u)!\n", ret); CloseHandle(m_req_queue); HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); return FALSE; } m_req_buffer = (LPBYTE)malloc(m_req_buffer_size); m_listener = listener; m_thread = AfxBeginThread(RecvRequestThread, this); return TRUE; } BOOL CHttpService::Delete(void) { if(m_req_queue) { CloseHandle(m_req_queue); HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); m_req_queue = NULL; } if(m_req_buffer) { free(m_req_buffer); m_req_buffer = NULL; } if(m_thread) { m_thread->Delete(); m_thread = NULL; } return TRUE; } BOOL CHttpService::SendRequestResp(HTTP_REQUEST_ID req_id, INT status, LPCSTR reason, LPCSTR type, HANDLE file, LPVOID mem, DWORD mem_size) { HTTP_RESPONSE resp; HTTP_DATA_CHUNK chunk; DWORD ret; DWORD sent; RtlZeroMemory(&resp, sizeof(resp)); resp.StatusCode = status; resp.pReason = reason; resp.ReasonLength = (USHORT)strlen(reason); resp.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = type; resp.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen(type); resp.EntityChunkCount = 1; resp.pEntityChunks = &chunk; if(file != NULL) { chunk.DataChunkType = HttpDataChunkFromFileHandle; chunk.FromFileHandle.FileHandle = file; chunk.FromFileHandle.ByteRange.StartingOffset.QuadPart = 0; chunk.FromFileHandle.ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF; } if(mem != NULL) { chunk.DataChunkType = HttpDataChunkFromMemory; chunk.FromMemory.pBuffer = mem; chunk.FromMemory.BufferLength = mem_size; } ret = HttpSendHttpResponse(m_req_queue, req_id, 0, &resp, NULL, &sent, NULL, 0, NULL, NULL); if(ret != NO_ERROR) { TRACE(L"HttpSendHttpResponse error(%u)!\n", ret); return FALSE; } return TRUE; } BOOL CHttpService::RecvRequestBody(HTTP_REQUEST_ID req_id, HANDLE file, char *mem, DWORD mem_size) { DWORD read; if(file != NULL) { return FALSE; } if(mem != NULL) { HttpReceiveRequestEntityBody(m_req_queue, req_id, 0, mem, mem_size, &read, NULL); mem[read] = 0; return TRUE; } return FALSE; } UINT CHttpService::RecvRequestThread(LPVOID param) { CHttpService *self; self = (CHttpService *)param; self->DoReceiveRequests(); Sleep(INFINITE); return 0; } BOOL CHttpService::DoReceiveRequests(void) { ULONG ret; DWORD bytes_read; PHTTP_REQUEST request; request = (PHTTP_REQUEST)m_req_buffer; while(1) { RtlZeroMemory(request,m_req_buffer_size); ret = HttpReceiveHttpRequest(m_req_queue,HTTP_NULL_ID,0,request,m_req_buffer_size,&bytes_read,NULL); if(ret == NO_ERROR) { m_listener->OnRecvRequest(this,request); continue; } TRACE("HttpReceiveHttpRequest error(%u)!\r\n", ret); if(ret == ERROR_OPERATION_ABORTED) { return FALSE; } if(ret == ERROR_INVALID_HANDLE) { return FALSE; } } return TRUE; } BOOL CHttpService::GetRemoteAddr(PHTTP_REQUEST request, CString &text) { PSOCKADDR_IN addr = (PSOCKADDR_IN)request->Address.pRemoteAddress; text.Format("%d.%d.%d.%d", addr->sin_addr.S_un.S_un_b.s_b1, addr->sin_addr.S_un.S_un_b.s_b2, addr->sin_addr.S_un.S_un_b.s_b3,addr->sin_addr.S_un.S_un_b.s_b4); return TRUE; }

     

    Processed: 0.013, SQL: 9