C/C++: OpenSSL实现https GET POST请求
前言
笔者这里有需求,需要用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请求相关推荐
- rhel5.5_Apache配置openssl支持https服务
<rhel5.5_Apache配置openssl支持https服务> 1:切换到openssl证书目录: # cd /etc/pki/tls/certs 2:创建私钥: # make se ...
- 基于openssl的https服务的配置
openssl实现私有CA,并配置基于openssl的https服务的配置,原理如下图 在CA服务器上实现私有CA步骤如下: 1.生成一对密钥 2.生成自签证书 基本的配置如下代码; [root@CA ...
- 深入理解HTTPS及在iOS系统中适配HTTPS类型网络请求(上)
2019独角兽企业重金招聘Python工程师标准>>> 深入理解HTTPS及在iOS系统中适配HTTPS类型网络请求 一.引言 本篇博客主要讨论如何在客户端与服务端之间进行HTTPS ...
- Swift - 使用SwiftHTTP通过HTTPS进行网络请求,及证书的使用
(本文代码已升级至Swift3) 一,证书的生成,以及服务器配置 参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书) 文章详细介绍了HTTPS,SSL/TL ...
- Swift - 使用Alamofire通过HTTPS进行网络请求,及证书的使用
(本文代码已升级至Swift3) 我原来写过一篇文章介绍如何使用证书通过SSL/TLS方式进行网络请求(Swift - 使用URLSession通过HTTPS进行网络请求,及证书的使用),当时用的是 ...
- JAVA通过HTTPS发送POST请求的方法
因为调用一个外部接口,会用到POST请求,而且还是Https的,但是由于之前学习的时候没有用到,所以研究了很久才弄懂了怎么去用JAVA实现Https发送post请求 使用的是HttpsURLConne ...
- Https的数据请求的证书设置
对于https的网络请求很多人都比较头疼,不止iOS包括pc端和移动端的很多请求都离不开https CFNetwork SSLHandshake failed (-9806) error = Erro ...
- Linux+Apache2+openssl实现https验证
首先安装SSL,再编译安装APACHE,再配置证书即可 1.下载apache和openssl 网址:http://www.apache.org http://www.openssl.org ...
- crt证书linux使用,linux下使用openssl生成https的crt和key证书
x509证书一般会用到三类文,key,csr,crt Key 是私用密钥openssl格,通常是rsa算法. Csr 是证书请求文件,用于申请证书.在制作csr文件的时,必须使用自己的私钥来签署申,还 ...
最新文章
- HDFS文件目录操作代码
- 财政 | 十一月财政总结
- 为什么接口在设计时所有的方法一般都要抛异常?
- inventory tool for Microsoft Uplates简介
- [读书笔记]小决心还是大决心
- linux ftp 服务配置
- php文本框如何设置高度,更改文本框高度?
- Hexo博客NexT主题开启文章目录和调整样式
- 最小生成树(Prim算法和Kruskal算法)
- gerrit rebase
- 助力无人船舶,开拓水上智能
- 剑指 Offer II 017. 含有所有字符的最短字符串
- bxl文件转换为AD可以用的原理图和PCB库文件
- 鸿蒙开发中vp和fp是啥?
- 团队开发背景及团队分工
- 电路中芯片旁边0.104uF电容的作用
- MySQL 索引失效详解
- selenium web端自动化测试框架环境搭建
- 输出1900——2000年中是闰年的年份呢
- python dcf估值_Python 常用模块
热门文章
- 中blur函数_Comonad在图像处理中的应用
- 1.5 编程基础之循环控制 35 求出e的值
- requirejs页面刷新失效js报错问题解决方案
- 基于asp.net的音乐分享网站的设计与实现(含源文件)
- Linux笔记-centos7编译安装svn 1.14.1
- canvas笔记-在canvas中使用其他HTML元素
- 前端笔记-vue cli中使用router-link进行路由跳转
- Linux学习笔记-最基础的常用shell命令
- java 多线程日志_多线程 打印的日志出现重复行
- matlab gui实例_App Designer 自学实例8