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

项目中用到的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


  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值