前言

笔者这里有需求,需要用C++实现 https 的GET、POST请求 以及GET下载文件 而且需要实现跨平台 在Linux、Windows都能正常运行。最好的是 只用一套代码 而不是根据具不同平台 跑不同代码,所以我们得找一个跨平台的支持https协议的库。都不用想,最好的当然是 OpenSSL啦。也有其他的比如libcurl 啥的,但是libcurl如果要支持https的也要链接OpenSSL了,还不如直接用OpenSSL。当然这里还有个不错的选择就是使用boost::asio库,但是笔者觉得使用上没有OpenSSL好用,故此没有用boost库。

网上示例代码比较多,笔者这里也是简单写了小demo,在Windows平台和Linux平台都能正常运行,只是为在互联网上多一个链接,让需要者多一篇可参考的博客而已。 这里使用的是openssl-1.0.2m 版本,OpenSSL更高的版本没有测试,想来 api改动应该不大。

代码

MyHttpsUtil.h

#pragma once
#include <string>
#include <openssl/ssl.h>enum REQ_TYPE
{GET_STRING = 0, // GET请求获取响应字符串POST_STRING,    // POST请求获得响应字符串GET_FILE        // GET请求下载文件
};class MyHttpsUtil
{public:virtual ~MyHttpsUtil(void);static MyHttpsUtil* getInstance();/* https get请求return value0:成功 strResponse为响应结果 -1:失败,strResponse为错误信息*/int getRequest(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, std::string& strResponse);/* https post请求return value0:成功 strResponse为响应结果 -1:失败,strResponse为错误信息*/int postRequest(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, std::string& strResponse);/* https get请求下载文件return value0:成功 -1:失败,strErrMsg为错误信息*/int getFile(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strErrMsg );private:MyHttpsUtil(void);private:// 建立TCP连接套接字int tcpConn(const char* pSvrIp, int iSvrPort, int& socket);/*发送数据到https服务器参数1:请求类型,决定最后3个参数的作用参数2:服务器IP参数3:服务器端口参数4:uri参数5:reqType:1 为POST_STRING时(POST请求) 请求参数,也就是请求体。参数6:reqType:2 为GET_FILE时(GET请求下载资源文件) 文件存储路径。参数7:reqType:0/1 为GET_STRING/POST_STRING 时(GET/POST请求响应字符串) 响应字符串在strResponse, 出现错误时 错误描述信息在strResponse中。*/int sendDataToSvr(REQ_TYPE reqType, const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strResponse );// 组装GET请求数据int getGetReqData(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, std::string& strReqData);// 组装POST请求数据int getPostReqData(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, std::string& strReqData);// 读取响应字符串int readResponseToString(SSL* ssl, std::string& strRespData);// 读取响应二进制数据到文件int readResponseToFile(SSL* ssl, const std::string& strFilePath, std::string& strErrMsg);
};

MyHttpsUtil.cpp

#include "MyHttpsUtil.h"
#ifdef WIN32
#include <winsock.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#endif#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>#define CODE_SUCCESS 0
#define CODE_FALID -1#ifdef WIN32
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#endifMyHttpsUtil::MyHttpsUtil(void)
{
}MyHttpsUtil::~MyHttpsUtil(void)
{
}MyHttpsUtil* MyHttpsUtil::getInstance()
{static MyHttpsUtil httpsClient;return &httpsClient;
}int MyHttpsUtil::getRequest(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, std::string& strResponse)
{return sendDataToSvr(GET_STRING, strSvrIp, iSvrPort, strUri, "", "", strResponse);
}int MyHttpsUtil::postRequest(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, std::string& strResponse)
{return sendDataToSvr(POST_STRING, strSvrIp, iSvrPort, strUri, strBody, "", strResponse);
}int MyHttpsUtil::getFile(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strErrMsg)
{return sendDataToSvr(GET_FILE, strSvrIp, iSvrPort, strUri, "", strFilePath, strErrMsg);
}int MyHttpsUtil::sendDataToSvr(REQ_TYPE reqType, const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strResponse)
{int iRet = CODE_FALID;int socketFd = 0;SSL_CTX* ctx = 0;SSL* ssl = 0;do {char* pSvrIp = NULL;struct hostent *pHostent = NULL;pHostent = gethostbyname(strSvrIp.c_str());if (pHostent == NULL){break;}pSvrIp = inet_ntoa(*(struct in_addr*)pHostent->h_addr_list[0]);// 1.建立TCP连接if( tcpConn(pSvrIp, iSvrPort, socketFd) != CODE_SUCCESS){break;}// 2.SSL初始化, 关联Socket到SSL,并建立连接SSL_library_init();OpenSSL_add_all_algorithms();SSL_load_error_strings(); ctx = SSL_CTX_new(SSLv23_client_method()); if(ctx == NULL){break;}ssl = SSL_new(ctx);SSL_set_fd(ssl, socketFd);int retCode = SSL_connect(ssl);if ( retCode != 1){int sslErrCode = SSL_get_error(ssl,retCode);strResponse = "SSL_connect error,openssl errCode = ";char errCode[11] = {0};sprintf_s(errCode, "%d", sslErrCode);strResponse.append(errCode);break;}std::string strReqData;if(GET_FILE == reqType || GET_STRING == reqType){getGetReqData(strSvrIp, iSvrPort, strUri, strReqData);}else{getPostReqData(strSvrIp, iSvrPort, strUri, strBody, strReqData);}// 4.通过SSL发送数据,数据量不大一次write就够了,如果是文件上传就需要循环写了。int writeLen = SSL_write(ssl, strReqData.c_str(), strReqData.length());  if (writeLen <=0){int sslErrCode = SSL_get_error(ssl,writeLen);strResponse = "SSL_write error,openssl errCode = ";char errCode[11] = {0};sprintf_s(errCode, "%d", sslErrCode);strResponse.append(errCode);break;}// 5.读取响应数据  int readLen = 0;char pHeader[1] = {0};int i = 0;// 响应头以\r\n\r\n 结束, 此处判断头是否传输完成while((readLen = SSL_read(ssl, pHeader, 1)) == 1){if(i < 4){if(pHeader[0] == '\r' || pHeader[0] == '\n'){i++;if(i >= 4){break;}}else{i = 0;}}}if( readLen < 0 ){int sslErrCode = SSL_get_error(ssl,readLen);strResponse = "SSL_read error,openssl errCode = ";char errCode[11] = {0};sprintf_s(errCode, "%d", sslErrCode);strResponse.append(errCode);break;}if(GET_FILE == reqType){iRet = readResponseToFile(ssl, strFilePath, strResponse);}else{iRet = readResponseToString(ssl, strResponse);}} while (false);// 6.关闭socket、断开连接if (socket){
#ifdef WIN32closesocket(socketFd);
#elseclose(socketFd);
#endif}if (ctx){SSL_CTX_free(ctx);}if (ssl){SSL_shutdown (ssl);SSL_free(ssl);}return iRet;
}int MyHttpsUtil::tcpConn(const char* pSvrIp, int iSvrPort, int& socket)
{socket = ::socket(AF_INET,SOCK_STREAM,0);if( socket == -1 ){return CODE_FALID;}sockaddr_in sa;sa.sin_addr.s_addr = inet_addr(pSvrIp);sa.sin_port = htons(iSvrPort);sa.sin_family = AF_INET;int retCode = ::connect(socket,(struct sockaddr*)&sa,sizeof(sa));  if(retCode == -1)  {  return CODE_FALID;}return CODE_SUCCESS;
}int MyHttpsUtil::getGetReqData(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, std::string& strReqData)
{char pLine[256] = {0};sprintf_s(pLine, "GET %s HTTP/1.1\r\n", strUri.c_str());strReqData.append(pLine);memset(pLine, 0, sizeof(pLine));sprintf_s(pLine, "Host: %s:%d\r\n",strSvrIp.c_str(), iSvrPort);strReqData.append(pLine);memset(pLine, 0, sizeof(pLine));strReqData.append("Accept: */*\r\n");strReqData.append("Connection: close\r\n\r\n");//strReqData.append("Connection: keep-alive\r\n\r\n");return CODE_SUCCESS;
}int MyHttpsUtil::getPostReqData(const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, std::string& strReqData)
{char pLine[256] = {0};sprintf_s(pLine, "POST %s HTTP/1.1\r\n", strUri.c_str());strReqData.append(pLine);memset(pLine, 0, sizeof(pLine));sprintf_s(pLine, "Host: %s:%d\r\n",strSvrIp.c_str(), iSvrPort);strReqData.append(pLine);memset(pLine, 0, sizeof(pLine));strReqData.append("Accept: */*\r\n");strReqData.append("Content-Type: application/json; charset=utf-8\r\n");memset(pLine, 0, sizeof(pLine));sprintf_s(pLine, "Content-Length: %d\r\n", strBody.length());strReqData.append("Connection: close\r\n\r\n");strReqData.append(strBody);return CODE_SUCCESS;
}int MyHttpsUtil::readResponseToString(SSL* ssl, std::string& strRespData)
{// 读取响应体数据,一次读1kchar pBody[1024 + 1] = {0};int readSize = sizeof(pBody) -1;int readLen = 0;while( (readLen = SSL_read(ssl, pBody, readSize)) > 0 ){strRespData.append(pBody);memset(pBody, 0, sizeof(pBody));}if(readLen < 0){int sslErrCode = SSL_get_error(ssl,readLen);strRespData = "SSL_read error,openssl errCode = ";char errCode[11] = {0};sprintf_s(errCode, "%d", sslErrCode);strRespData.append(errCode);return CODE_FALID;}strRespData.append(pBody);return CODE_SUCCESS;
}int MyHttpsUtil::readResponseToFile(SSL* ssl, const std::string& strFilePath, std::string& strErrMsg)
{FILE *fp = fopen(strFilePath.c_str(), "wb+");if(fp == NULL){strErrMsg = "fopen error,filePath:";strErrMsg.append(strFilePath);return CODE_FALID;}char pBody[1024 + 1] = {0};int readSize = sizeof(pBody) -1;int readLen = 0;while( (readLen = SSL_read(ssl, pBody, readSize)) > 0 ){if( fwrite(pBody, 1, readLen, fp) != readLen){strErrMsg = "fwrite error";return CODE_FALID;}memset(pBody, 0, sizeof(pBody));}if(readLen < 0){int sslErrCode = SSL_get_error(ssl,readLen);strErrMsg = "SSL_read error,openssl errCode = ";char errCode[11] = {0};sprintf_s(errCode, "%d", sslErrCode);strErrMsg.append(errCode);return CODE_FALID;}if( fwrite(pBody, 1, readLen, fp) != readLen){strErrMsg = "fwrite error";return CODE_FALID;}fclose(fp);return CODE_SUCCESS;
}

测试

这里我们以 https://www.baidu.com 进行测试 GET、POST、GET下载文件,接口均正常。

test.cpp

#include "MyHttpsUtil.h"
#include <iostream>int main(int argc, char* argv[])
{std::string strResponse;if( MyHttpsUtil::getInstance()->getRequest("www.baidu.com", 443, "/", strResponse) == 0){//std::cout << strResponse << std::endl; // 太长了,下个输出无法输出std::cout << "get req suc" << std::endl;}else{std::cout << "get req error" << std::endl;}std::cout << "=======================" << std::endl;strResponse.clear();if( MyHttpsUtil::getInstance()->postRequest("www.baidu.com", 443, "/", "{}", strResponse) == 0){std::cout << "post req suc" << std::endl;std::cout << strResponse << std::endl;}else{std::cout << "post req error" << std::endl;}std::cout << "=======================" << std::endl;std::string strErrMsg;if( MyHttpsUtil::getInstance()->getFile("www.baidu.com", 443, "/", strResponse,"c:\\1.txt", strErrMsg) == 0 ){std::cout << "get req downlaod file suc" << std::endl;}else{std::cout << "get req downlaod file error:" << strErrMsg << std::endl;}std::cout << "=======================" << std::endl;getchar();return 0;
}


C/C++: OpenSSL实现https GET POST请求相关推荐

  1. rhel5.5_Apache配置openssl支持https服务

    <rhel5.5_Apache配置openssl支持https服务> 1:切换到openssl证书目录: # cd /etc/pki/tls/certs 2:创建私钥: # make se ...

  2. 基于openssl的https服务的配置

    openssl实现私有CA,并配置基于openssl的https服务的配置,原理如下图 在CA服务器上实现私有CA步骤如下: 1.生成一对密钥 2.生成自签证书 基本的配置如下代码; [root@CA ...

  3. 深入理解HTTPS及在iOS系统中适配HTTPS类型网络请求(上)

    2019独角兽企业重金招聘Python工程师标准>>> 深入理解HTTPS及在iOS系统中适配HTTPS类型网络请求 一.引言 本篇博客主要讨论如何在客户端与服务端之间进行HTTPS ...

  4. Swift - 使用SwiftHTTP通过HTTPS进行网络请求,及证书的使用

    (本文代码已升级至Swift3) 一,证书的生成,以及服务器配置 参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书) 文章详细介绍了HTTPS,SSL/TL ...

  5. Swift - 使用Alamofire通过HTTPS进行网络请求,及证书的使用

    (本文代码已升级至Swift3) 我原来写过一篇文章介绍如何使用证书通过SSL/TLS方式进行网络请求(Swift - 使用URLSession通过HTTPS进行网络请求,及证书的使用),当时用的是 ...

  6. JAVA通过HTTPS发送POST请求的方法

    因为调用一个外部接口,会用到POST请求,而且还是Https的,但是由于之前学习的时候没有用到,所以研究了很久才弄懂了怎么去用JAVA实现Https发送post请求 使用的是HttpsURLConne ...

  7. Https的数据请求的证书设置

    对于https的网络请求很多人都比较头疼,不止iOS包括pc端和移动端的很多请求都离不开https CFNetwork SSLHandshake failed (-9806) error = Erro ...

  8. Linux+Apache2+openssl实现https验证

    首先安装SSL,再编译安装APACHE,再配置证书即可 1.下载apache和openssl 网址:http://www.apache.org       http://www.openssl.org ...

  9. crt证书linux使用,linux下使用openssl生成https的crt和key证书

    x509证书一般会用到三类文,key,csr,crt Key 是私用密钥openssl格,通常是rsa算法. Csr 是证书请求文件,用于申请证书.在制作csr文件的时,必须使用自己的私钥来签署申,还 ...

最新文章

  1. HDFS文件目录操作代码
  2. 财政 | 十一月财政总结
  3. 为什么接口在设计时所有的方法一般都要抛异常?
  4. inventory tool for Microsoft Uplates简介
  5. [读书笔记]小决心还是大决心
  6. linux ftp 服务配置
  7. php文本框如何设置高度,更改文本框高度?
  8. Hexo博客NexT主题开启文章目录和调整样式
  9. 最小生成树(Prim算法和Kruskal算法)
  10. gerrit rebase
  11. 助力无人船舶,开拓水上智能
  12. 剑指 Offer II 017. 含有所有字符的最短字符串
  13. bxl文件转换为AD可以用的原理图和PCB库文件
  14. 鸿蒙开发中vp和fp是啥?
  15. 团队开发背景及团队分工
  16. 电路中芯片旁边0.104uF电容的作用
  17. MySQL 索引失效详解
  18. selenium web端自动化测试框架环境搭建
  19. 输出1900——2000年中是闰年的年份呢
  20. python dcf估值_Python 常用模块

热门文章

  1. 中blur函数_Comonad在图像处理中的应用
  2. 1.5 编程基础之循环控制 35 求出e的值
  3. requirejs页面刷新失效js报错问题解决方案
  4. 基于asp.net的音乐分享网站的设计与实现(含源文件)
  5. Linux笔记-centos7编译安装svn 1.14.1
  6. canvas笔记-在canvas中使用其他HTML元素
  7. 前端笔记-vue cli中使用router-link进行路由跳转
  8. Linux学习笔记-最基础的常用shell命令
  9. java 多线程日志_多线程 打印的日志出现重复行
  10. matlab gui实例_App Designer 自学实例8