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

 

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