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;
}