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

 

热门文章

暂无图片
编程学习 ·

PCL点云焊点提取

滚回来更新一篇文章,和各位交流一下 待处理点云: 数量级:百万 类型:零部件 描述:弯曲表面上有一些凸起在上面,需要提取凸起和平面接触的一圈点云,作为焊接的加工点参考:https://zhuanlan.zhihu.com/p/32111069其实这篇文章也算是全面了,思路和他的差不多,只是算法不太一…
暂无图片
编程学习 ·

【数据结构】-排序-快速排序

~快速排序在平均情况下是效果最好的排序算法~每趟子表的排序都是从两头向中间交替逼近,接下来举一个例子类别排序方法最好时间最坏时间平均时间空间复杂度稳定性序列特征适用于插入排序直接插入排序n(顺序)n2(逆序)n21稳定有序序列+待排序元素+无序序列基本有序/n很小折半插…
暂无图片
编程学习 ·

PAT 1161 Merging Linked Lists

原题链接:暂无 关键词:链表 Given two singly linked lists L 1 =a 1 →a 2 →…→a n−1 →a n L1=a1→a2→…→an−1→an and L 2 =b 1 →b 2 →…→b m−1 →b m L2=b1→b2→…→bm−1→bm . If n≥2m n≥2m , you are supposed to reverse and merge the shorter one i…
暂无图片
编程学习 ·

大数据运维-监控可视化需求构建总结

1 数据源需求1.1 主动接入需求来源:某些市场银行1.1.1 API 接入能力要求 支持请求授权接入。(auth 认证)支持数据快速获取测试验证。支持数据依赖的参数引用。支持全量、增量同步(日、时、分、秒)支持多层级解析 (目前按JSON格式处理) 1.1.2 SDK接入能力要求 支持快速集…
暂无图片
编程学习 ·

用Python读取pg数据库,准确统计每一张表的数据量,输出中英文表名和数据量

1 前言 在我们工作中,有时候老板关系我们手上到底有多少数据,每一张表中到底有多少数据量,整个库又有多少数据量?要给他一个准确的数据,给出一张详细清单。 在网上遇到的一种做法是使用navicat写SQL语句统计pg_class里面的reltuples这个列数据,但是发现这个数据有很大偏…
暂无图片
编程学习 ·

获取电脑ip地址的代码工具类

获取ip地址的工具类 import javax.servlet.http.HttpServletRequest;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springfram…
暂无图片
编程学习 ·

提高复杂网络分析效率!中国科学家研发强化学习新框架

提高复杂网络分析效率!中国科学家研发强化学习新框架近日,中国国防科技大学、美国加州大学洛杉矶分校和哈佛医学院的研究人员研发了一个深度强化学习框架FINDER。相比于现有的解决方案,FINDER能够更快速、更高效地找到复杂网络中一组最关键的节点,进而使复杂网络以较高的效…
暂无图片
编程学习 ·

前端面试题及答案.全全全!!!---js

HTML——CSS——JS——es6——Vue——微信小程序-----------服务器----------nodeJS面试题1.基本数据类型有哪几种?undefined,null,boolean,string,number,Symbol(es6)2.引用数据类型有哪些?Object,Array3.JavaScript的typeof返回的数据类型?string number array object fun…
暂无图片
编程学习 ·

解析!JNPF快速开发平台是什么,可以开发什么软件系统

最近,一款叫JNPF的软件在网上的关注度似乎一直很火热,很多业内人士都在使用或在讨论这个JNPF软件。那JNPF到底是一款什么样的软件,它能够做什么?其实,与其说JNPF是一款软件,更确切的说,它是一个软件快速开发平台。它有很多强大的功能特点,它专注于企事业单位的商务办公…
暂无图片
编程学习 ·

1.古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子, * 小兔子长到第三个月后每个月又生一对兔子, * 假如兔子都不死,问每个月的兔子对数为多少? * 分析: * 月份:1 2 3

package com.ujiuye.java;/*古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,* 小兔子长到第三个月后每个月又生一对兔子,* 假如兔子都不死,问每个月的兔子对数为多少?* 分析:* 月份:1 2 3 4 5 6 7 8 9...* 对数 :1 1 2 3 5 8 13 21... */ public class Dem…
暂无图片
编程学习 ·

【LEETCODE】718.最长重复子数组-动态规划+滑动窗口

题目 给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。 思路 设数组A长度为n,B长度为m动态规划: 设置动态规划数组dp[n+1][m+1],dp[n][m]=0。 从A[n-1]和B[m-1]开始向前遍历比较,可以得出伪代码: if A[i]==B[j]:dp[i][j]=dp[i+1][j+1]+1; else if …
暂无图片
编程学习 ·

windows系统远程提权提升、MySQL UDF提权

本文目录权限提升提权本质提权分类windows系统提权基础命令windows提权辅助工具辅助工具介绍windows远程提权上手操作一下Mysql UDF提权udf介绍udf.dll获取上传udf执行提权命令 前言 小白一枚,之前听过的比较厉害的操作就是提权,维权。今天学习一下。 权限提升 提权本质 提权…
暂无图片
编程学习 ·

C语言指针笔记

C语言指针 一.地址与指针变量 程序在执行过程中需要有内存来存储需要用到的数据和程序代码,它们都占据一些内存单元,地址是这些内存单元的编号,同时包括它所指向的数据的类型信息。因此,可以把地址形象化地称为"指针"。 但不要把地址和指针混为一个概念,地址是数…
暂无图片
编程学习 ·

Java 中内部类简单概念

概念:在一个类内部定义的类 1.内部类会生成独立的.class文件。文件名Outer$Inner.class 2.内部类可以访问外部类的私有属性,从而不破坏外部类的封装性 成员内部类(类比成员变量) 1.定义位置:类以内,方法以外 2.如何创建对象 Outer o = new Outer(); //创建外部类对象 Out…
暂无图片
编程学习 ·

期末复习、化学反应工程科目(第八、九章)

@Author:Runsen @Date:2020/7/2人生最重要的不是所站的位置,而是内心所朝的方向。只要我在每篇博文中写得自己体会,修炼身心;在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰难,奋勇前行,不忘初心,砥砺前行,人生定会有所收获,不留遗憾 (作者:Runsen )作者介…
暂无图片
编程学习 ·

Nginx教程

Nginx教程 一、Nginx配置文件 user nginx; worker_processes 1;error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;# 以上统称为全局块 # worker_processes他的数值越大,Nginx的并发能力就越强 # error_log 代表Nginx的错误日志存放的位置 events
暂无图片
编程学习 ·

功能丰富的推荐系统

功能丰富的推荐系统 Feature-Rich Recommender Systems 交互数据是用户偏好和兴趣的最基本指示。在以前引入的模型中起着至关重要的作用。然而,交互数据通常非常稀疏,有时可能会有噪声。为了解决这个问题,可以在推荐模型中集成一些附加信息,比如条目的特性、用户的概要信息…
暂无图片
编程学习 ·

float类型的数据和无穷小的比较

float类型的数据和无穷小的比较 coding 时,偶尔需要对浮点型数据进行比较,尤其是判断两个浮点型数据是否一致时,需要把两个浮点数之差的绝对值和float.Epsilon比较 其中 float.Epsilon是大于零的最小浮点数(大约为1.401298E-45) 以判断浮点数只能为 1 -1 和 0 为例 //和 1 -…
暂无图片
编程学习 ·

pyspark入门整理

最近工作需要对千万以上数据做特征处理,为了提升运(zao)算(ri)效(xia)率(ban),开始使用pyspark做分布式运算。也是从基本开始学习,先把用到的一些资料贴在这里,日后有空结合业务进一步整理。(一)原理篇一文弄懂PySpark原理与实践 https://blog.csdn.net/oTengYue…
暂无图片
编程学习 ·

比特币协会圆满举办两届BSV编程马拉松,多个团队脱颖而出,荣获奖金及投资

活动时间:2019年5月4日-5月5日、2019年8月17日-8月19日 活动主办方:比特币协会 活动合作方:CoinGeek 活动评审:nChain2019年,比特币协会举办了两场线上虚拟编程马拉松,这是针对开发者的限时的编程比赛,参赛者都是为了赢得丰厚的奖金池。在编程马拉松期间,参赛者的任务是…