C++用socket实现简单的http请求

学习了几天http相关的东西,用C++实现了一个简单的 HTTP请求

1 . HttpRes.h
//
//  HttpReq.hpp
//  HttpClient
//
//  Created by LiYong on 2018/1/23.
//

#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
//
//  HttpReq.cpp
//  HttpClient
//
//  Created by LiYong on 2018/1/23.
//

#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)
{
    //判断URL是否有效
    if (strUrl == "")
    {
        debugOut("URL为空\n");
        return 0;
    }
    //限制URL的长度
    if (URLSIZE < strUrl.size())
    {
        debugOut("URL的长度不能超过:%d\n",URLSIZE);
        return 0;
    }

    //创建HTTP协议表头
    string strHttpHead = httpHeadCreate(strMethod, strUrl, strData);

    if (m_iSocketFd != INVALID_SOCKET)
    {
        string strResult = httpDataTransmit(strHttpHead, m_iSocketFd);
        if (strResult != "")
        {
            strResponse = strResult;
            return 1;
        }
    }
    //创建Socket
    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;
}
//发送HTTP请求并且接受响应
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;
            }
        }
    }
}
//从URL中获取Host地址
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;
}
//从HTTP请求URL中获取HTTP请求参数
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;
}
//从HTTP请求URL中获取端口号
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;
}
//从Http请求URL中获取IP地址
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;
    }

    //判断是否为点分十进制IP,否则通过域名获取IP
    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;
        }
    }
}
//检查socketFd是否为可写不可读状态
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)
    {
        //判断SocketFd是否为可写不可读状态
        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;//select出错,所有描述符集清0
    }
    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
//
//  main.cpp
//  HttpClient
//
//  Created by LiYong on 2018/1/23.
//

#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但搭建。

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现一个简单HTTP服务器,可以使用Windows平台上的C/C++编程语言。 首先,你需要了解HTTP协议的基本知识,例如请求和响应的格式、HTTP方法(GET、POST等)、状态码以及标头字段(例如Content-Type、Content-Length等)。 然后,你可以使用Winsock库来创建一个基于套接字的服务器。使用`socket`函数创建一个套接字,然后使用`bind`函数将其绑定到指定的IP地址和端口号。 接下来,你可以使用`listen`函数开始监听传入的连接请求。当有客户端发送连接请求时,你可以使用`accept`函数接受该连接,并返回一个新的套接字,用于与客户端进行通信。 在收到客户端请求后,你可以使用`recv`函数接收HTTP请求。解析请求的首部和URI路径,并根据请求确定要执行的操作。 对于GET请求,你可以打开请求的文件,并将文件内容作为HTTP响应返回给客户端。你可以使用`fopen`函数打开文件,然后使用`fread`函数读取文件内容,并使用`send`函数发送响应到客户端。 对于POST请求,你可以解析请求体中的数据,并根据需求执行相应的操作。例如,你可以解析表单数据、存储到数据库或者进行其他处理。 最后,你可以使用`closesocket`函数关闭套接字,并释放相关资源。 需要注意的是,这只是一个简单HTTP服务器的实现示例,如果你想要一个更完整和稳定的服务器,你可能需要处理并发连接、多线程或者使用更成熟的HTTP服务器框架。 总结起来,实现一个简单的Windows平台上的C/C++ HTTP服务器涉及到使用Winsock库创建套接字、接受连接请求、解析HTTP请求、处理请求并发送响应等步骤。希望这个回答对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值