1. 程式人生 > >c++實現簡單的Http客戶端協議,WebRequest

c++實現簡單的Http客戶端協議,WebRequest

      最近要寫一個代理程式,軟體最終要跑在嵌入式裝置上,其中一部分是需要做一個簡單爬蟲程式,用來操作嵌入式裝置的Web服務上的資訊,我不想用第三方的任何庫,如是簡單看了下http協議,用一天時間實現了http協議的客戶端,實現Get,Post,UpFile(檔案上傳)等常用操作,需要完善的部分是Cookie沒有自動提取和傳輸,需要自己手動處理,朋友們可以完善吧!寫個日誌,便於日後參考!希望對朋友有參考。

       由於最終要在嵌入式裝置上執行,所以用 #define Windows 來區分,Socket部分Window和Linux還是有差別的,程式相容了Windows和Liunx。

通訊類標頭檔案

#pragma once
#include<string>
#ifndef WINDOWS
#define WINDOWS
#endif 

#ifdef WINDOWS
	#include<WINSOCK2.H>
	#include <WS2tcpip.h>  
#else
    #include <iconv.h>
	#include <netinet/in.h>
	#include <stdio.h>
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <arpa/inet.h>
	#include <string.h>
	#include <stdlib.h>
	#include <sys/stat.h>
	#include <unistd.h>
	#include <fcntl.h>
   
#endif // WINDOWS
using namespace std;
class TCPClient
{
public:
	
	TCPClient(string host);
	~TCPClient();


	bool ConnectToServer();
	bool SendData(const char * sendBuffer,int dataLen);
	string RecvData();
private:

	#ifdef WINDOWS
		SOCKET  clientSocket;
	#else
	int  clientSocket;
	#endif
	string hostAddr;
	bool isConnected;
};

封裝基礎通訊類cpp

#include "TCPClient.h"
#include<vector>
#include<errno.h>

#ifdef WINDOWS
#pragma warning(disable:4996) 
#pragma comment(lib, "WS2_32.lib")
#endif // WINDOWS

#ifdef  WINDOWS

TCPClient::TCPClient(string host)
{
	hostAddr = host;
	isConnected = false;
#ifdef WINDOWS
	WSAData wsdata;
	if (WSAStartup(MAKEWORD(2, 2), &wsdata) != 0)
	{
		WSACleanup();
		printf("WSAStartup failured \r\n");
	}
	else
	{
		printf("WSAStartup OK\r\n");
	}

#endif
}

bool TCPClient::ConnectToServer()
{
	if (isConnected)
		return true;

	clientSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == -1)
	{
		printf("sockclient create fail ! \n");
		return false;
	}

	struct sockaddr_in dest_addr;     /* 目的地址*/
	dest_addr.sin_family = AF_INET;   /* host byte order */
	dest_addr.sin_port = htons(80);   /* short, network byte order */

#ifdef WINDOWS
	dest_addr.sin_addr.s_addr = inet_addr(this->hostAddr.c_str());
#else
	inet_pton(AF_INET, this->hostAddr.c_str(), &dest_addr.sin_addr.s_addr);
#endif // WINDOWS

	if (connect(clientSocket, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) < 0)
	{

#ifdef WINDOWS
		closesocket(clientSocket);
		printf("connect controlServer failured%d\r\n", GetLastError());

#else
		close(clientSocket);
		printf("connect controlServer failured\r\n");

#endif

		return false;
	}
	isConnected = true;
	return true;
}

TCPClient::~TCPClient()
{
#ifdef WINDOWS
	closesocket(clientSocket);
	WSACleanup();
#else
	close(clientSocket);
#endif
}

bool TCPClient::SendData(const char *sendBuffer, int dataLen)
{
	if (!ConnectToServer())
		return false;
	int sendLen = 0;
	int oneLen = 0;
	while (sendLen < dataLen)
	{
		oneLen = send(this->clientSocket, sendBuffer + sendLen, dataLen - sendLen, 0);
		if (oneLen <= 0)
		{
			if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
			{
				//這幾種錯誤碼,認為連線是正常的,繼續接收
			}
			else {
				//printf("%d", WSAGetLastError());
				return 0;
			}

			break;
		}
		else
			sendLen += oneLen;
	}
	return true;
}

string TCPClient::RecvData()
{
	char  recvData[1000] = { 0 };
	string responseData = "";
	int recLen = 0;
	int recLenOfAll = 0;
	while ((recLen = recv(this->clientSocket, recvData, sizeof(recvData) - 1, 0)) > 0)
	{
		responseData.append(recvData);
		memset(recvData, 0, 1000);
		//ZeroMemory(recvData, 1000);
		recLenOfAll += recLen;
	}
	string resp;
#ifdef WINDOWS
	resp =responseData.c_str();
	closesocket(this->clientSocket);
#else
	resp = (responseData.c_str());
	close(this->clientSocket);
#endif
	isConnected = false;
	return resp;
}

WebRequest標頭檔案

#pragma once

#include<iostream>
#include<thread>
#include<mutex>
#include<map>
#include<string>
#include "TCPClient.h"

using namespace std;

enum  RequestType
{
	Post, Get
};

class WebRequest
{
private:
	string SendData(string method, string path, map<string, string> paramters, string referUrl);
	string SendData(string method, string path, string bodyInfo, string referUrl);
	string SendData(string method, string path, string referUrl);
public:
	WebRequest(string host);
	~WebRequest();
	
	string GetData(string path, map<string, string> paramters, string referUrl);
	string GetData(string path, string referUrl);
	string PostData(string path, map<string, string> paramters, string referUrl);
	string PostData(string path, string referUrl);
	string GetData(string path, string bodyInfo, string referUrl);
	string PostData(string path, string bodyInfo, string referUrl);

	string UpFile(string path, string FileName);
public:
	map<string, string>  Headers;
	RequestType requestType;
private:
	string GetHeadContent();
	TCPClient *tcpClient;
	string hostAddr;
};

webrequest.cpp

#include "WebRequest.h"
#include<vector>
#include<fstream>
WebRequest::WebRequest(string host)
{
	this->hostAddr = host;
	//Headers.insert(pair<string,string>("A", "B"));
	map<string, string> *HeadersTest = new map<string, string>();
	Headers["Accept"]="*/*";
	Headers["Host"] = host;
	Headers["Accept-Language"] = "zh-CN";
	Headers["Accept-Encoding"] = "gzip, deflate";
	Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";
	Headers["Connection"] = "Keep-Alive";
	tcpClient = new TCPClient(host);
}

WebRequest::~WebRequest()
{
	delete tcpClient;
}


string WebRequest::GetHeadContent()
{
	string headContent = "";
	for (const auto& kv : this->Headers)
	{
		if (kv.second.size() == 0)
			continue;
		headContent.append(kv.first);
		headContent.append(": ");
		headContent.append(kv.second);
		headContent.append("\r\n");
	}
	return headContent;
}

string WebRequest::SendData( string method, string path, string bodyInfo, string referUrl)
{
	string headStr = "";
	headStr.append(method+" " + path + " HTTP/1.1\r\n");
	headStr.append(GetHeadContent());
	if (referUrl.length() > 0)
		headStr.append("Referer: http://"+hostAddr + referUrl +"\r\n");
	headStr.append("Content-Length: " + to_string(bodyInfo.size()));
	headStr.append("\r\n\r\n");
	headStr.append(bodyInfo);
	if (tcpClient->SendData(headStr.c_str(), headStr.length()))
		return tcpClient->RecvData();
	else
		return "傳送請求失敗!";
}
string WebRequest::GetData(string path, map<string, string> paramters, string referUrl)
{
	string recstr = this->SendData("GET", path, paramters, referUrl);
	//this->temRecStr = recstr;
	return recstr;
}
string WebRequest::PostData(string path, map<string, string> paramters, string referUrl)
{
	return this->SendData("POST", path, paramters, referUrl);
}
string WebRequest::GetData(string path, string referUrl)
{
	return this->SendData("GET", path, referUrl);
}
string WebRequest::PostData(string path,  string referUrl)
{
	return this->SendData("POST", path, referUrl);
}

string WebRequest::GetData(string path, string bodyInfo, string referUrl)
{
	return this->SendData("GET", path, bodyInfo, referUrl);
}
string WebRequest::PostData(string path, string bodyInfo, string referUrl)
{
	return this->SendData("POST", path, bodyInfo, referUrl);
}
string WebRequest::SendData(string method, string path, map<string,string> paramters,string referUrl)
{
	string headStr = "";
	headStr.append(method+" "+ path+" HTTP/1.1\r\n");
	headStr.append(GetHeadContent());
	if (referUrl.length() > 0)
		headStr.append("Referer: http://" +hostAddr+ referUrl + "\r\n");
	string bodyInfo = "";
	
	for (const auto& kv : paramters)
	{
		//if(TString::endsWith(path,"&"))
		if (bodyInfo.length() > 0)
			bodyInfo.append("&");
		bodyInfo.append(kv.first);
		bodyInfo.append("=");
		bodyInfo.append(kv.second);
	}

	headStr.append("Content-Length: " + to_string(bodyInfo.size()));
	headStr.append("\r\n\r\n");
	headStr.append(bodyInfo);

	if (this->tcpClient->SendData(headStr.c_str(), headStr.length()))
		return tcpClient->RecvData();
	else
		return"請求失敗!";
}
string WebRequest::SendData(string method, string path,  string referUrl)
{
	string headStr = "";
	headStr.append(method + " " + path + " HTTP/1.1\r\n");
	headStr.append(GetHeadContent());
	if (referUrl.length() > 0)
		headStr.append("Referer: http://" + hostAddr + referUrl + "\r\n");
	headStr.append("Content-Length:0" );
	headStr.append("\r\n\r\n");

	if (this->tcpClient->SendData(headStr.c_str(), headStr.length()))
		return tcpClient->RecvData();
	else
		return"請求失敗!";
}
string WebRequest::UpFile(string path, string fileName)
{
	string boundary = "---------------------------7d33a816d302b6\r\n";
	this->Headers["Content-Type"] = "multipart/form-data;boundary=---------------------------7d33a816d302b6";// +boundary;

	string bodyInfo = "";
	fstream fs;
	fs.open(fileName, ios::binary | ios::in);
	if (!fs)
	{
		cout << "檔案不存在!" << fileName << endl;
		return  "上傳檔案不存在!";
	}

	fs.seekg(0, fs.end);
	int fsLen=fs.tellg();
	bodyInfo.append(boundary);//檔案內容開始,
	bodyInfo.append("Content-Disposition: form-data; name=\"fx_19V_11.10.128.14.uot\"; filename=\"" + fileName + "\"\r\n");
	bodyInfo.append("Content-Type: application/octet-stream\r\n");
	
	string sendContent = "";
	sendContent.append("POST " + path + " HTTP/1.1\r\n");
	sendContent.append(GetHeadContent());
	sendContent.append("Content-Length: " + to_string(fsLen + bodyInfo.length() + boundary.length()));
	sendContent.append("\r\n\r\n");
	sendContent.append(bodyInfo);

	const char* sendBuffer = sendContent.c_str();
	if (!tcpClient->SendData(sendBuffer, sendContent.length()))
		return "傳送請求失敗";

	//這裡讀取檔案,傳送檔案
	fs.seekg(0, fs.beg);
	char fsBuffer[2000];
	int readLen = 0;
	int sendAll = 0;
	while (!fs.eof())
	{
		fs.read(fsBuffer, 2000);
		readLen=fs.gcount();
		if (!tcpClient->SendData(fsBuffer, readLen))
			break;
		else
		{
			sendAll += readLen;
			cout <<"已傳送:"<< sendAll << endl;
		}
	}
	fs.close();
	if(!tcpClient->SendData(("\r\n"+boundary).c_str(), boundary.length()))//協議結束
		return "傳送請求失敗";
	return tcpClient->RecvData();
}