学习了几天http相关的东西,用C++实现了一个简单的 HTTP请求
1 . HttpRes.h
#ifndef HttpReq_hpp
#define HttpReq_hpp
#include <iostream>
#include <stdio.h>
#include <string>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#define BUFSIZE 4096
#define URLSIZE 2048
#define INVALID_SOCKET -1
#define __DEBUG__
using namespace std ;
class HttpRes
{
public :
HttpRes();
~HttpRes();
static HttpRes *getInstance();
void debugOut(string fmt,...);
int httpGet(string strUrl,string &strResponse);
int httpPost(string strUrl,string strData,string &strResponse);
private :
int httpResquestExec(string strMethod,string strUrl,string strData,string &strResponse);
string httpHeadCreate(string strMethod,string strUrl,string strData);
string httpDataTransmit(string strHttpHead,int isSocFd);
int getPortFromUrl(string strUrl);
string getIpFromUrl(string strUrl);
string getParamFromUrl(string strUrl);
string getHostAddFromUrl(string strUrl);
int socketFdCheck(const int iSockFd);
static int m_iSocketFd;
};
#endif /* HttpReq_hpp */
2.HttpReq.cpp
#include "HttpReq.hpp"
HttpRes::HttpRes()
{
m_iSocketFd = INVALID_SOCKET;
}
HttpRes::~HttpRes()
{
}
HttpRes *HttpRes::getInstance()
{
HttpRes *http = new HttpRes();
if (http)
{
return http;
}
return nullptr ;
}
int HttpRes::httpGet(string strUrl, string &strResponse)
{
return httpResquestExec("GET" , strUrl, "" , strResponse);
}
int HttpRes::httpPost(string strUrl,string strData, string &strResponse)
{
return httpResquestExec("POST" ,strUrl, strData, strResponse);
}
int HttpRes::httpResquestExec(string strMethod,string strUrl,string strData, string &strResponse)
{
if (strUrl == "" )
{
debugOut("URL为空\n" );
return 0 ;
}
if (URLSIZE < strUrl.size())
{
debugOut("URL的长度不能超过:%d\n" ,URLSIZE);
return 0 ;
}
string strHttpHead = httpHeadCreate(strMethod, strUrl, strData);
if (m_iSocketFd != INVALID_SOCKET)
{
string strResult = httpDataTransmit(strHttpHead, m_iSocketFd);
if (strResult != "" )
{
strResponse = strResult;
return 1 ;
}
}
m_iSocketFd = INVALID_SOCKET;
m_iSocketFd = socket(AF_INET, SOCK_STREAM, 0 );
if (m_iSocketFd < 0 )
{
debugOut("socket error! Error code: %d,Error message: %s\n" ,errno,strerror(errno));
return 0 ;
}
int iPort = getPortFromUrl(strUrl);
if (iPort < 0 )
{
debugOut("获取URL端口失败\n" );
return 0 ;
}
string strIP = getIpFromUrl(strUrl);
if (strIP == "" )
{
debugOut("从URL获取IP地址失败\n" );
return 0 ;
}
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(iPort);
if (inet_pton(AF_INET,strIP.data(), &servaddr.sin_addr) <= 0 )
{
debugOut("inet_pton error ! Error code: %d,Error message:%s\n" ,errno,strerror(errno));
close(m_iSocketFd);
m_iSocketFd = INVALID_SOCKET;
return 0 ;
}
int flags = fcntl(m_iSocketFd, F_SETFL, 0 );
if (fcntl(m_iSocketFd, F_SETFL,flags|O_NONBLOCK) == -1 )
{
close(m_iSocketFd);
m_iSocketFd = INVALID_SOCKET;
debugOut("fcntl error! Error code: %d,Error message: %s" ,errno,strerror(errno));
return 0 ;
}
int iRet = connect(m_iSocketFd, (struct sockaddr *)&servaddr, sizeof (servaddr));
if (iRet == 0 )
{
string strResult = httpDataTransmit(strHttpHead, m_iSocketFd);
if (NULL == strResult.c_str())
{
close(m_iSocketFd);
m_iSocketFd = INVALID_SOCKET;
return 0 ;
}
else
{
strResponse = strResult;
return 1 ;
}
}
else if (iRet < 0 )
{
if (errno != EINPROGRESS)
{
return 0 ;
}
}
iRet = socketFdCheck(m_iSocketFd);
if (iRet > 0 )
{
string strResult = httpDataTransmit(strHttpHead, m_iSocketFd);
if (strResult == "" )
{
close(m_iSocketFd);
m_iSocketFd = INVALID_SOCKET;
return 0 ;
}
else
{
strResponse = strResult;
return 1 ;
}
}
else
{
close(m_iSocketFd);
m_iSocketFd = INVALID_SOCKET;
return 0 ;
}
return 1 ;
}
string HttpRes::httpHeadCreate(string strMethod,string strUrl,string strData)
{
string strHost = getHostAddFromUrl(strUrl);
string strParam = getParamFromUrl(strUrl);
string strHttpHead;
strHttpHead.append(strMethod);
strHttpHead.append(" /" );
strHttpHead.append(strParam);
strHttpHead.append(" HTTP/1.1\r\n" );
strHttpHead.append("Accept: */*\r\n" );
strHttpHead.append("Accept-Language: cn\r\n" );
strHttpHead.append("User-Agent: Mozilla/4.0\r\n" );
strHttpHead.append("Host: " );
strHttpHead.append(strHost);
strHttpHead.append("\r\n" );
strHttpHead.append("Cache-Control: no-cache\r\n" );
strHttpHead.append("Connection: Keep-Alive\r\n" );
if (strMethod == "POST" )
{
char len[8 ] = {0 };
unsigned long iLen = strData.size();
sprintf (len, "%lu" ,iLen);
strHttpHead.append("Content-Type: application/x-www-form-urlencoded\r\n" );
strHttpHead.append("Content-Length: " );
strHttpHead.append(len);
strHttpHead.append("\r\n\r\n" );
strHttpHead.append(strData);
}
strHttpHead.append("\r\n\r\n" );
return strHttpHead;
}
string HttpRes::httpDataTransmit(string strHttpHead,int isSocFd)
{
char *buf = (char *)malloc (BUFSIZ);
memset (buf, 0 , BUFSIZ);
char *head = (char *)strHttpHead.data();
long ret = send(isSocFd, (void *)head, strlen (head)+1 , 0 );
if (ret < 0 )
{
debugOut("send error ! Error code: %d,Error message: %s\n" ,errno,strerror(errno));
close(isSocFd);
return nullptr ;
}
while (1 )
{
ret = recv(isSocFd, (void *)buf, BUFSIZ, 0 );
if (ret == 0 )
{
close(isSocFd);
free (buf);
return nullptr ;
}
else if (ret > 0 )
{
string strRecv = buf;
free (buf);
return strRecv;
}
else if (ret < 0 )
{
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue ;
}
else
{
close(isSocFd);
free (buf);
return nullptr ;
}
}
}
}
string HttpRes::getHostAddFromUrl(string strUrl)
{
char url[URLSIZE] = {0 };
strcpy (url, strUrl.c_str());
char *strAddr = strstr (url, "http://" );
if (strAddr == NULL)
{
strAddr = strstr (url, "https://" );
if (strAddr != NULL)
{
strAddr += 8 ;
}
}
else
{
strAddr += 7 ;
}
if (strAddr == NULL)
{
strAddr = url;
}
char * strHostAddr = (char *)malloc (strlen (strAddr) +1 );
memset (strHostAddr, 0 , strlen (strAddr) + 1 );
for (int i = 0 ; i < strlen (strAddr) + 1 ; i++)
{
if (strAddr[i] == '/' )
{
break ;
}
else
{
strHostAddr[i] = strAddr[i];
}
}
string host = strHostAddr;
free (strHostAddr);
return host;
}
string HttpRes::getParamFromUrl(string strUrl)
{
char url[URLSIZE] = {0 };
strcpy (url, strUrl.c_str());
char *strAddr = strstr (url, "http://" );
if (strAddr == NULL)
{
strAddr = strstr (url, "https://" );
if (strAddr != NULL)
{
strAddr += 8 ;
}
}
else
{
strAddr += 7 ;
}
if (strAddr == NULL)
{
strAddr = url;
}
char *strParam = (char *)malloc (strlen (strAddr)+1 );
memset (strParam, 0 , strlen (strAddr)+1 );
int iPos = -1 ;
for (int i = 0 ; i < strlen (strAddr)+1 ; i++)
{
if (strAddr[i] == '/' )
{
iPos = i;
break ;
}
}
if (iPos == -1 )
{
strcpy (strParam, "" );
}
else
{
strcpy (strParam, strAddr+iPos+1 );
}
string param = strParam;
free (strParam);
return param;
}
int HttpRes::getPortFromUrl(string strUrl)
{
int nPort = -1 ;
char *strHostAddr = (char *)getHostAddFromUrl(strUrl).data();
if (strHostAddr == NULL)
{
return -1 ;
}
char strAddr[URLSIZE] = {0 };
strcpy (strAddr, strHostAddr);
char *strPort = strchr (strAddr, ':' );
if (strPort == NULL)
{
nPort = 80 ;
}
else
{
nPort = atoi(++strPort);
}
return nPort;
}
string HttpRes::getIpFromUrl(string strUrl)
{
string url = getHostAddFromUrl(strUrl);
char *strHostAddr = (char *)url.data();
char *strAddr = (char *)malloc (strlen (strHostAddr) + 1 );
memset (strAddr, 0 , strlen (strAddr)+1 );
int nCount = 0 ;
int nFlag = 0 ;
for (int i = 0 ; i < strlen (strAddr) + 1 ; i++)
{
if (strHostAddr[i] == ':' )
{
break ;
}
strAddr[i] = strHostAddr[i];
if (strHostAddr[i] == '.' )
{
nCount++;
continue ;
}
if (nFlag == 1 )
{
continue ;
}
if ((strHostAddr[i] >= 0 )||(strHostAddr[i] <= '9' ))
{
nFlag = 0 ;
}
else
{
nFlag = 1 ;
}
}
if (strlen (strAddr) <= 1 )
{
return NULL;
}
if ((nCount == 3 ) && (nFlag == 0 ))
{
return strAddr;
}
else
{
struct hostent *he = gethostbyname(strAddr);
free (strAddr);
if (he == NULL)
{
return NULL;
}
else
{
struct in_addr** addr_list = (struct in_addr **)he->h_addr_list;
for (int i = 0 ; addr_list[i] != NULL; i++)
{
return inet_ntoa(*addr_list[i]);
}
return NULL;
}
}
}
int HttpRes::socketFdCheck(const int iSockFd)
{
struct timeval timeout ;
fd_set rset,wset;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(iSockFd, &rset);
FD_SET(iSockFd, &wset);
timeout.tv_sec = 3 ;
timeout.tv_usec = 500 ;
int iRet = select(iSockFd+1 , &rset, &wset, NULL, &timeout);
if (iRet > 0 )
{
int iW = FD_ISSET(iSockFd,&wset);
int iR = FD_ISSET(iSockFd,&rset);
if (iW && !iR)
{
char error[4 ] = "" ;
socklen_t len = sizeof (error);
int ret = getsockopt(iSockFd,SOL_SOCKET,SO_ERROR,error,&len);
if (ret == 0 )
{
if (!strcmp (error, "" ))
{
return iRet;
}
else
{
debugOut("%s %s %d\tgetsockopt error code:%d,error message:%s" , __FILE__, __FUNCTION__, __LINE__, errno, strerror(errno));
}
}
else
{
debugOut("%s %s %d\tgetsockopt failed. error code:%d,error message:%s" , __FILE__, __FUNCTION__, __LINE__, errno, strerror(errno));
}
}
else
{
debugOut("%s %s %d\tsockFd是否在可写字符集中:%d,是否在可读字符集中:%d\t(0表示不在)\n" , __FILE__, __FUNCTION__, __LINE__, iW, iR);
}
}
else if (iRet == 0 )
{
return 0 ;
}
else
{
return -1 ;
}
return -2 ;
}
void HttpRes::debugOut(string fmt, ...)
{
#ifdef __DEBUG__
va_list ap;
va_start(ap, fmt);
vprintf (fmt.c_str(), ap);
va_end(ap);
#endif
}
int HttpRes::m_iSocketFd = INVALID_SOCKET;
3.main.cpp
#include <iostream>
#include "HttpReq.hpp"
using namespace std ;
int main(int argc, const char * argv[])
{
HttpRes *http = HttpRes::getInstance();
string url = "http://192.168.0.72:8000/" ;
string return_msg;
if (http->httpGet(url , return_msg))
{
cout << return_msg <<endl;
}
return 0 ;
}
响应服务端我是用Django但搭建。