MFC調用winhttp實現簡易的HTTP服務器程序

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章