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