C++实现简单的HTTP客户端(阻塞方式)

2023-05-16

项目中用到的HTTP请求功能,自己简单写了个客户端,实现了POST方式,GET方式实现应该也很简单(空接口已经写好:=))。

应该支持多线程(这个很重要)。

HttpClient.h

#ifndef _HTTP_CLIENT_H_
#define _HTTP_CLIENT_H_

#define HTTP_DEFAULT_REQUEST_TIMEOUT		(60*1000)
#define HTTP_DEFAULT_PORT					80
#define HTTP_SEND_BUF_LEN					2048
#define HTTP_RECV_BUF_LEN					2048

class HttpClient
{
public:
	HttpClient();
	~HttpClient();

public:
	/*********************************************************************
	功能:以阻塞方式发送一个HTTP请求,并返回请求结果
	返回值:返回发送成功或者失败
	参数:url	请求的HTTP url,不能为空。
		  post_data	 POST数据,可以为空,如果为空,则使用GET方式发送。
		  time_out   发送超时时间,如果为0,则使用默认值
		  ret_buf    请求返回结果缓冲区
		  ret_len    请求返回结果缓冲区长度
	***********************************************************************/
	bool Send(const char* url, const char* post_data, const int time_out, char* ret_buf, int* ret_len);

private:
	char		m_strIP[MAX_PATH];				/*HTTP服务器地址*/
	int			m_iPort;						/*HTTP服务器端口*/
	char		m_strAction[MAX_PATH];			/*HTTP请求的动作*/
	int			m_iTimeOut;						/*HTTP请求超时时间*/

	SOCKET		m_sock;							/*TCP 套接字*/


	/*********************************************************************
	功能:实现TCP连接HTTP服务器
	返回值:返回发送成功或者失败
	参数:空
	***********************************************************************/
	bool TCP_Connect();

	/*********************************************************************
	功能:实现TCP连接关闭
	返回值:空
	参数:空
	***********************************************************************/
	void TCP_Close();

	/*********************************************************************
	功能:设置阻塞TCP连接的超时时间
	返回值:返回发送成功或者失败
	参数:空
	***********************************************************************/
	bool TCP_SetTimeout();

	/*********************************************************************
	功能:解析URL中的IP地址,端口,文件名等。
	返回值:返回发送成功或者失败
	参数:url	请求的HTTP url,不能为空。
	***********************************************************************/
	bool ParserUrl(const char* url);

	/*********************************************************************
	功能:实现HTTP POST方法
	返回值:返回发送成功或者失败
	参数:post_data	 POST数据,不能为空
		  ret_buf	 HTTP返回数据
		  ret_len	 HTTP返回数据长度
	***********************************************************************/
	bool Post(const char* post_data, char* ret_buf, int* ret_len);

	/*********************************************************************
	功能:实现HTTP GET方法
	返回值:返回发送成功或者失败
	参数:ret_buf	 HTTP返回数据
		  ret_len	 HTTP返回数据长度
	***********************************************************************/
	bool Get(char* ret_buf, int* ret_len);

	/*********************************************************************
	功能:生成POST方法的HTTP包
	返回值:空
	参数:post_data	 POST数据
		  buf		 HTTP数据缓冲区
		  buf_len	 传入缓冲区长度,传出HTTP数据长度
	***********************************************************************/
	void MakePostBuf(const char* post_data, char* buf, int* buf_len);

	/*********************************************************************
	功能:初始化Win32网络库
	返回值:空
	参数:空
	***********************************************************************/
	inline void InitWin32NetLib()
	{
		WSADATA wsa_data;
		WSAStartup(MAKEWORD(2,0), &wsa_data);
	}

	/*********************************************************************
	功能:注销Win32网络库
	返回值:空
	参数:空
	***********************************************************************/
	inline void UnInitWin32NetLib()
	{
		WSACleanup();
	}

private:
	static char* m_post_header;
	static char* m_get_header;

public:
	static HttpClient* GetInstance();
private:
	static HttpClient m_oInstance;
};


#endif


HttpClient.cpp

#include "stdafx.h"
#include "HttpClient.h"
#include <Log/MyLogEx.h>


HttpClient HttpClient::m_oInstance;

HttpClient* HttpClient::GetInstance()
{
	return &m_oInstance;
}

char* HttpClient::m_post_header = "POST %s HTTP/1.1\r\n"
    "Accept: image/gif, image/jpeg, */*\r\nAccept-Language: zh-cn\r\n"
    "Accept-Encoding: gzip, deflate\r\nHost: %s:%d\r\n"
	"Content-Type: application/json\r\nContent-Length: %d\r\n"
    "User-Agent: HLS Slice Service\r\nConnection: Keep-Alive\r\n\r\n%s";

char* HttpClient::m_get_header = "GET %s HTTP/1.1\r\n"
    "Accept: image/gif, image/jpeg, */*\r\nAccept-Language: zh-cn\r\n"
    "Accept-Encoding: gzip, deflate\r\nHost: %s:%d\r\n"
    "User-Agent: HLS Slice Service\r\nConnection: Keep-Alive\r\n\r\n";

HttpClient::HttpClient()
	: m_iPort(HTTP_DEFAULT_PORT),
	  m_iTimeOut(HTTP_DEFAULT_REQUEST_TIMEOUT),
	  m_sock(INVALID_SOCKET)
{
	memset(m_strIP, 0, sizeof(m_strIP));
	memset(m_strAction, 0, sizeof(m_strAction));

	InitWin32NetLib();
}

HttpClient::~HttpClient()
{
	if(m_sock != INVALID_SOCKET)
	{
		closesocket(m_sock);
		m_sock = INVALID_SOCKET;
	}

	UnInitWin32NetLib();
}

bool HttpClient::Send(const char* url, const char* post_data, const int time_out, char* ret_buf, int* ret_len)
{
	if(url == NULL || ret_buf == NULL || *ret_len == 0)
	{
		return false;
	}

	if(!ParserUrl(url))
	{
		LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "Parser HTTP URL failed!");
		return false;
	}

	//LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "HTTP IP=%s, port=%d, filename=%s",
	//			m_strIP, m_iPort, m_strAction);

	m_iTimeOut = (time_out == 0 ? HTTP_DEFAULT_REQUEST_TIMEOUT : time_out);

	if(post_data)
	{
		return Post(post_data, ret_buf, ret_len);
	}
	
	return Get(ret_buf, ret_len);
}

bool HttpClient::ParserUrl(const char* url)
{
	char szBuf[1024] = {0};
	strncpy_s(szBuf, sizeof(szBuf)-1, url, sizeof(szBuf)-1);
	int length = 0;
	char port_buf[20];
	char *buf_end = (char *)(szBuf + strlen(szBuf));
	char *begin, *host_end, *colon, *question_mark;

	/* 查找主机的开始位置 */
	begin = strstr(szBuf, "//");
	begin = (begin ? begin + 2 : szBuf);

	colon = strchr(begin, ':');
	host_end = strchr(begin, '/');

	if(host_end == NULL)
	{
		host_end = buf_end;
	}
	else
	{   /* 得到文件名 */
		question_mark = strchr(host_end, '?');
		if(question_mark != NULL)
		{
			strncpy_s(m_strAction, MAX_PATH-1, host_end, question_mark-host_end);
		}
		else
		{
			strncpy_s(m_strAction, MAX_PATH-1, host_end, strlen(host_end));
		}
	}

	if(colon) /* 得到端口号 */
	{
		colon++;

		length = host_end - colon;
		memcpy(port_buf, colon, length);
		port_buf[length] = 0;
		m_iPort = atoi(port_buf);

		host_end = colon - 1;
	}
	else
	{
		m_iPort = HTTP_DEFAULT_PORT;
	}

	/* 得到主机信息 */
	length = host_end - begin;
	length = (length > MAX_PATH  ? MAX_PATH : length);
	memcpy(m_strIP, begin, length);
	m_strIP[length] = 0;

	return true;
}

void HttpClient::MakePostBuf(const char* post_data, char* buf, int* buf_len)
{
	sprintf_s(buf, *buf_len, m_post_header, m_strAction, m_strIP, m_iPort, strlen(post_data), post_data);
	*buf_len = strlen(buf);

	//LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "POST Data=%s", buf);
}

bool HttpClient::TCP_SetTimeout()
{
	int TimeOut=m_iTimeOut;

	if(::setsockopt(m_sock, SOL_SOCKET, SO_SNDTIMEO,(char *)&TimeOut, sizeof(TimeOut))==SOCKET_ERROR)
	{
		LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "TCP设置发送超时失败!");
		return false;
	}

	if(::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut))==SOCKET_ERROR)
	{
		LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "TCP设置接收超时失败!");
		return false;
	}

	return true;
}

bool HttpClient::TCP_Connect()
{
	int result = 0;
	struct sockaddr_in serv_addr;

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons((u_short)m_iPort);
	serv_addr.sin_addr.s_addr = inet_addr(m_strIP);

	m_sock = socket(AF_INET, SOCK_STREAM, 0); 
	if(m_sock == INVALID_SOCKET)
	{
		LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "socket() 调用失败!错误码=%d", WSAGetLastError());
		return false;
	}

	if(!TCP_SetTimeout())
	{
		return false;
	}

	result = connect(m_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
	if(result == SOCKET_ERROR)
	{
		closesocket(m_sock);
		m_sock = INVALID_SOCKET;
		//LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "连接失败!错误码=%d", WSAGetLastError());
		return false; 
	}

	return true;
}

void HttpClient::TCP_Close()
{
	closesocket(m_sock);
	m_sock = INVALID_SOCKET;
}

bool HttpClient::Post(const char* post_data, char* ret_buf, int* ret_len)
{
	int iRet;
	char strSendBuf[HTTP_SEND_BUF_LEN] = {0};
	int iSendLen = HTTP_SEND_BUF_LEN;
	MakePostBuf(post_data, strSendBuf, &iSendLen);

	if(!TCP_Connect())
	{
		return false;
	}

	iRet = send(m_sock, strSendBuf, iSendLen, 0);
	if (iRet == SOCKET_ERROR)
	{
		LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "发送数据失败!错误码=%d", WSAGetLastError());
		TCP_Close();
		return false; 
	}

	int recv_len = *ret_len;
	iRet = recv(m_sock, ret_buf, recv_len, 0);
	if(iRet > 0)
	{
		*ret_len = iRet;
	}
	else if(iRet == 0)
	{
		//LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "Server close connection");
	}
	else
	{
		//LOG_PRINTEX(0, MyLogEx::LOG_LEVEL_DEBUG_4, "error == %d", WSAGetLastError());
		*ret_len = 0;
	}

	TCP_Close();

	return true;
}

bool HttpClient::Get(char* ret_buf, int* ret_len)
{
	return false;
}


代码下载(实现了GET方法和URL编码的代码):http://download.csdn.net/download/fang437385323/9942803


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++实现简单的HTTP客户端(阻塞方式) 的相关文章

  • 解决chrome添加扩展时的报错:“此项内容已下载并添加到Chrome中”

    chrome是google家的服务 xff0c 下个扩展也是要折腾一番 xff0c 网络质量更是不能保证 xff0c 所以下点东西时不时会出错 这次在下一个扩展的时候发现装了好久还是显示 正在检查 xff0c 遂手动刷新了一下页面 xff0
  • Manjaro终端无法输入中文,亲测有效

    span class token function export span GTK IM MODULE span class token operator 61 span fcitx span class token function ex
  • sh: 1: vue-cli-service: Permission denied

    看报错日志 xff0c 权限被拒绝 进入node modules bin 34 ll 34 查看一下会发现该文件 vue cli service 34 并没有可执行权限 chmod R 755
  • Linux系统Fcitx中文输入法开机启动方法

    Linux系统Fcitx中文输入法开机启动方法 在GNOME下的启动在KDE下的启动 Debian FC Ubuntu的默认中文输入法都是SCIM xff0c 其实也挺好用的 xff0c 有点类似windows下微软拼音输入法 xff0c
  • linux sftp文件上传与下载

    何为sftp sftp是Secure File Transfer Protocol的缩写 xff0c 安全文件传送协议 可以为传输文件提供一种安全的加密方法 回到顶部 连接 linux下直接在终端中输入 xff1a sftp usernam
  • win10专业版 原版安装教程

    WINDOWS10 的安装很是辛酸 xff0c 折腾了很久 xff0c 写下教程 xff0c 以防以后再入坑 Notes 不建议安装Ghost版 xff0c 会有许多问题 xff0c 电脑升级内存条后 xff0c 发现电脑有时候莫名奇妙蓝屏
  • Qt面试以及常用类继承关系图

    关于Qt的事件 事件的产生 xff1a 产生来源有timer事件外设的事件 xff08 mouseMoveEvent xff09 timer事件 xff0c 滚轮事件 xff0c 界面重绘制事件等等事件的接受与处理 xff1a QObjec
  • 无人驾驶虚拟仿真(四)--通过ROS系统控制小车行走

    简介 xff1a 实现键盘控制虚拟仿真小车移动 xff0c w s a d 空格 xff0c 对应向前 向后 向左 向右 急停切换功能 xff0c q键退出 1 创建key control节点 进入工作空间源码目录 xff1a cd myr
  • 云台控制协议

    PELCO D与PELCO P协议 PELCO D 数据格式 xff1a 1位起始位 8位数据 1位停止位 xff0c 无校验位 波特率 xff1a 2400B S 命令格式 xff1a 字节1 字节2 字节3 字节4 字节5 字节6 字节
  • 继承中子类与父类构造\析构的调用和顺序

    1 子类被构造的时候会先调用父类的构造函数 2 子类析构的时候先析构子类后析构父类 3 如果直接用子类构造一个父类的对象 删除这个父类的对象不会调用子类的析构函数 xff0c 这就是引入虚析构函数的原因 xff01
  • 28335GPIO及外部中断配置介绍

    弄了两周终于把28335的启动流程 寄存器及中断向量表的映射方法 内存的划分等有了一个全面的了解 xff0c 今天看到久违的LED灯的闪烁 xff0c 顿扫阴霾 特在此总结下28335GPIO及外部中断配置介绍 其实对于一个微控制器 xff
  • DSP28335与AD7606通过SPI的串行数据交互

    弄了三天的DSP28335与AD7606的通信终于实现了 最终的方案是通过DSP28335控制AD7606的采样 xff0c 采集的数据通过SPI串口发送给28335 xff0c 然后28335通过串口发送给上位机显示 其实程序第一天就写好
  • 利用28335的epwm产生spwm波的总结

    一 SPWM设计简介 设计的内容是产生倍频的SPWM波 xff0c 也即是用的是同一个调制波 xff0c 两个桥臂上的载波相差180度 产生spwm时 xff0c 利用TB产生载波 xff0c 也即是三角波 xff08 计数方式采用增减模式
  • 段错误总结

    最近试着写了华为编程大赛的程序 xff0c 出现较多的一个问题是段错误 xff0c 由此看来对指针与边界的处理还不熟练 网上有些总结的很不错 xff0c 因此结合网上资料整理下 xff08 下面的还有些地方没有深究 xff0c 有时间继续深
  • 启发式算法总结

    下面是一些学习到的算法 xff0c 有些没有具体用到 xff0c 所以只是概念的解释 xff0c 方便自己以后回忆 一 粒子群算法 1 1基本思想 粒子群算法是模拟群体智能所建立起来的一种优化算法 xff0c 粒子群算法可以用鸟类在一个空间
  • 线程、进程通信再总结

    下面这个部分摘抄自网上 xff0c 谢谢贡献的作者 一 进程间的通信方式 管道 pipe xff1a 管道是一种半双工的通信方式 xff0c 数据只能单向流动 xff0c 而且只能在具有亲缘关系的进程间使用 进程的亲缘关系通常是指父子进程关
  • 结构体类型的动态数组操作

    链接 xff1a https www nowcoder com questionTerminal 6fc9a928c7654b0fbc37d16b8bd29ff9 来源 xff1a 牛客网 假如我们有3种月饼 xff0c 其库存量分别为18
  • 基于Linkit 7697的红绿灯控制系统

    1 硬件准备 LinkIt 7697 1 xff0c 继电器模块 1 xff0c 面包板 1 xff0c RGB LED灯 1 xff08 共阳极 xff0c 工作电流20mA xff0c 红灯压降2 2 2V xff0c 绿灯蓝灯压降3
  • 利用背包问题解决的双核处理问题

    一种双核CPU的两个核能够同时的处理任务 xff0c 现在有n个已知数据量的任务需要交给CPU处理 xff0c 假设已知CPU的每个核1秒可以处理1kb xff0c 每个核同时只能处理一项任务 n个任务可以按照任意顺序放入CPU进行处理 x
  • 简单整蛊室友,只需几行bat病毒代码

    为了让整蛊更方便 xff0c 不能搞什么花里胡哨 xff0c 所有直接使用bat代码来编写 首先新建1个txt文件 xff0c 更改为任意名称 xff0c 但后缀名必须更改为bat或com 然后右键编辑 再输入以下代码 xff1a star

随机推荐

  • 四轴飞行器,PID调节过程心得记录

    初次接触四轴 xff0c 编写四轴的姿态PID控制部分 xff0c xff0c 横滚俯仰是把遥控器的杆量转换为目标角度 xff0c 然后目标角度PID运算转换为目标角速度 xff0c 然后目标角速度PID运算转换为电机输出量 xff0c x
  • 天地飞接收机输出信号解析

    今天测试了下天地飞8通道的接收机的pwm输出 接收机的输出信号 xff0c 可以按照50HZ的pwm信号来解析 xff0c 在stm32中 xff0c 使用外部高地电平触发中断的方式 xff0c 来记录一个脉宽的时间 用示波器实际查看信号的
  • Linux-TCP之深入浅出send和recv

    概念 先明确一个概念 xff1a 每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区 xff0c TCP的全双工的工作模式以及TCP的滑动窗口便是依赖于这两个独立的buffer以及此buffer的填充状态 接收缓冲区把数据缓
  • popen使用方法及场景

    1 popen的应用场景 popen应用于执行shell命令 xff0c 并读取此命令的返值 xff0c 或者与执行的命令进行交互 2 popen的实现 popen 函数通过创建一个管道 xff0c 调用fork 产生一个子进程 xff0c
  • sscanf函数使用详解

    一 描述 sscanf通常被用来解析并转换字符串 xff0c 其格式定义灵活多变 xff0c 可以实现很强大的字符串解析功能 sscanf的原型 include lt stdio h gt int sscanf const char str
  • Linux通过系统函数设置系统时间

    一 描述 通过settimeofday 函数来设置系统时间 xff0c 这个函数设置的精度可以精确到微秒 include lt time h gt int settimeofday const struct timeval tv const
  • 用Eclipse完成C语言编程的几个简单步骤

    Eclipse是一款被广泛应用的开发工具 xff0c 最初它是为编写Java程序而设计的 xff0c 但由于它良好的架构并作为开源软件来发行 xff0c 有很多的公司和个人以它为基础开发了插件 xff0c 使得Eclipse有了越来越丰富的
  • 最适合程序员转行的10大职业

    三十而立 xff0c 源自 论语 为政 xff0c 说的是人到了30岁就应该去面对生活中的一切困难 而对于软件开发领域的从业者来说 xff0c 30岁 xff0c 却是一道槛 30岁以后 xff0c 适合程序员的工作到底是什么 专家和大家一
  • ROS2学习笔记(二)-- 多机通讯原理简介及配置方法

    在ROS1中由主节点 master 负责其它从节点的通信 xff0c 在同一局域网内通过设置主节点地址也可以实现多机通讯 xff0c 但是这种多机通讯网络存在一个严重的问题 xff0c 那就是所有从节点强依赖于主节点 xff0c 一旦运行主
  • C语言--“.”与“->”有什么区别?

    这虽然是个小问题 xff0c 但有时候很容易让人迷惑 xff0c 因为有的时候用混淆了 xff0c 程序编译不通过 下面说说我对它们的理解 一般情况下用 xff0c 只需要声明一个结构体 格式是 xff0c 结构体类型名 43 结构体名 然
  • C语言--函数指针的用法总结

    函数指针的由来 一个函数在编译时被分配一个入口地址 xff0c 这个入口地址就称为函数的指针 函数名代表函数的入口地址 xff0c 这一点和数组一样 我们可以用一个指针变量来存放这个入口地址 xff0c 然后通过该指针变量调用函数 如 xf
  • std::mutex 用法详解

    Mutex 又称互斥量 xff0c C 43 43 11中与 Mutex 相关的类 xff08 包括锁类型 xff09 和函数都声明在 lt mutex gt 头文件中 xff0c 所以如果你需要使用 std mutex xff0c 就必须
  • 成功解决TypeError: a bytes-like object is required, not 'str'

    解决问题 TypeError a bytes like object is required not 39 str 39 解决思路 问题出在python3 5和Python2 7在套接字返回值解码上有区别 python bytes和str两
  • 计算机网络笔记:字节序与比特序

    1 字节序 所谓字节序是指多字节数据在内存中的存储顺序 xff0c 通常有两种 xff1a 小端字节序 大端字节序 xff1b 小端字节序 xff1a 低位字节存放在低位地址 xff0c 高位字节存放在高位地址 xff1b 大端字节序 xf
  • 全网最全的Postman接口自动化测试!(菜鸟级攻略)

    一 背景 该篇文章针对已经掌握 Postman 基本用法的读者 xff0c 即对接口相关概念有一定了解 已经会使用 Postman 进行模拟请求的操作 当前环境 xff1a Window 7 64Postman 版本 xff08 免费版 x
  • 接口测试之Postman使用全指南(原来使用 Postman测试API接口如此简单)

    Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件 Postman背景介绍 用户在开发或者调试网络程序或者是网页B S模式的程序的时候是需要一些方法来跟踪网页请求的 xff0c 用户可以使用一些网络的监视工具比如
  • 常用Qt类的继承图

  • extern “C”{}的含义及解决的问题

    C 与C 43 43 程序连接问题 它们之间的连接问题主要是因为c c 43 43 编绎器对函数名译码的方式不能所引起的 xff0c 考虑下面两个函数 c int strlen char string c 43 43 int strlen
  • ROS2学习笔记(四)-- 用方向键控制小车行走

    简介 xff1a 在上一节的内容中 xff0c 我们通过ROS2的话题发布功能将小车实时视频信息发布了出来 xff0c 同时使用GUI工具进行查看 xff0c 在这一节内容中 xff0c 我们学习一下如何订阅话题并处理话题消息 xff0c
  • C++实现简单的HTTP客户端(阻塞方式)

    项目中用到的HTTP请求功能 xff0c 自己简单写了个客户端 xff0c 实现了POST方式 xff0c GET方式实现应该也很简单 xff08 空接口已经写好 xff1a 61 xff09 应该支持多线程 xff08 这个很重要 xff