环境: window10_x64 & vs2022
python版本: 3.9.13
日常开发中,会遇到c/c++作为客户端访问http/https服务的情况,今天整理下windows10环境下c/c++使用winhttp访问http/https服务的笔记,并提供相关资源下载。
我将从以下几个方面展开:
- http服务构建
- https证书生成及服务构建
- winhttp使用示例
- 资源下载
一、模拟http服务端
1、构建http服务
这里使用tornado来构建http服务。
示例代码(httpServer1.py):
#! /usr/bin/env python3 #-*- coding:utf-8 -*- import tornado.ioloop import tornado.web import tornado.httpserver import json,time,datetime settings = { "debug" : False , } def trace(reqType,reqBody): msg = str(datetime.datetime.now()) + " , [" + reqType + "] , " + str(reqBody) print(msg) class MainHandler(tornado.web.RequestHandler): def get(self): trace("get",self.request.arguments) #query = self.get_query_argument("query") #print("query : %s" % query) self.set_header('content-type', 'application/json') #self.write(json.dumps({"result" : "test message"})) self.finish(json.dumps({"result" : "get message"})) def put(self): trace("put",self.request.body) self.finish(json.dumps({"result" : "put message"})) def post(self): #trace("post",self.request.arguments) trace("post",self.request.body) self.set_header('Content-type', 'application/json') #self.set_header('Transfer-Encoding', 'chunked') self.finish(json.dumps({"result" : "post message %d" % time.time()})) if __name__ == "__main__": port = 8093 print("listen on port %d"%port) application = tornado.web.Application([ (r"/.*", MainHandler), ],**settings) application.listen(port) #http_server = tornado.httpserver.HTTPServer(application) #http_server.bind(port,"0.0.0.0") #http_server.start(num_processes=0) tornado.ioloop.IOLoop.instance().start()
2、服务运行效果
curl测试命令如下:
curl -v http://127.0.0.1:8093 && echo ""
运行效果如下:
二、模拟https服务端
构建https服务,需要使用证书,这里使用自签名证书来实现。
1、生成证书
生成自签名证书(genCertTest1.bat):
goto start cat > openssl.cnf <<EOF [req] distinguished_name = req_distinguished_name x509_extensions = v3_req prompt = no [req_distinguished_name] C = CN ST = ShangHai L = ShangHai O = MyCompany OU = Mike_Zhang@live.com CN = localhost [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = localhost DNS.2 = 127.0.0.1 IP.1 = 127.0.0.1 EOF :start :: 生成包含SAN的证书 openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes -config openssl.cnf -extensions v3_req
openssl.cnf文件内容如下:
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = CN
ST = ShangHai
L = ShangHai
O = MyCompany
OU = Mike_Zhang@live.com
CN = localhost
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = 127.0.0.1
IP.1 = 127.0.0.1
执行 genCertTest1.bat 脚本,会生产证书文件:
2、构建https服务
这里基于tornado来实现,添加ssl相关内容即可。
示例代码如下(httpsServer1.py):
#! /usr/bin/env python3 #-*- coding:utf-8 -*- import tornado.ioloop import tornado.web import tornado.httpserver import json,time,datetime import ssl settings = { "debug" : False , } def trace(reqType,reqBody): msg = str(datetime.datetime.now()) + " , [" + reqType + "] , " + str(reqBody) print(msg) class MainHandler(tornado.web.RequestHandler): def get(self): trace("get",self.request.arguments) #query = self.get_query_argument("query") #print("query : %s" % query) self.set_header('content-type', 'application/json') #self.write(json.dumps({"result" : "test message"})) self.finish(json.dumps({"result" : "get message"})) def put(self): trace("put",self.request.body) self.finish(json.dumps({"result" : "put message"})) def post(self): #trace("post",self.request.arguments) trace("post",self.request.body) self.set_header('Content-type', 'application/json') self.finish(json.dumps({"result" : "post message %d" % time.time()})) if __name__ == "__main__": #ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ssl_ctx.load_cert_chain( certfile="server.crt", keyfile="server.key" ) ssl_ctx.options |= ( ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION ) # 设置现代加密套件 ssl_ctx.set_ciphers( 'ECDHE-ECDSA-AES256-GCM-SHA384:' 'ECDHE-RSA-AES256-GCM-SHA384:' 'ECDHE-ECDSA-CHACHA20-POLY1305:' 'ECDHE-RSA-CHACHA20-POLY1305:' 'DHE-RSA-AES256-GCM-SHA384' ) # 设置椭圆曲线 ssl_ctx.set_ecdh_curve('prime256v1') # 必需的安全设置 port = 8443 print("listen on port %d"%port) app = tornado.web.Application([ (r"/.*", MainHandler), ],**settings) http_server = tornado.httpserver.HTTPServer(app, ssl_options=ssl_ctx) #http_server.listen(port) http_server.bind(port,"0.0.0.0") http_server.start() tornado.ioloop.IOLoop.current().start()
3、服务运行效果
curl测试命令如下(忽略证书):
curl -vk https://127.0.0.1:8443 && echo ""
运行效果如下:
三、使用WinHTTP实现客户端
1、WinHTTP说明
Microsoft Windows HTTP 服务 (WinHTTP) 提供 HTTP 客户端应用程序编程接口 (API),以便通过 HTTP 协议将请求发送到其他 HTTP 服务器。
该服务提供 C/C++ 应用程序编程接口 (API) ,支持HTTPS协议。
文档地址:
https://learn.microsoft.com/zh-cn/windows/win32/winhttp/winhttp-start-page
c/c++接口文档地址:
https://learn.microsoft.com/zh-cn/windows/win32/winhttp/using-the-winhttp-c-c---api
2、使用示例
关键点:
1)使用 WinHttpConnect 函数创建连接;
2)使用WinHttpOpenRequest设置get/post方法、http/https协议等参数;
3)WinHttpAddRequestHeaders 设置http自定义请求头;
4)WinHttpSendRequest发送数据;
示例代码如下(winhttpTest1.cpp):
完整代码可从如下渠道获取:
四、资源获取