Win C++ Socket模拟HTTP协议GET和POST,解决body内容不全,做了HTTP内容解析笔记

做完win socket后续修改实现Linux(jni,android)

萌新做笔记,请大佬勿喷,弟弟我屁话也不多,就不做详解了

QuickHttp.cpp核心

//
// Created by Administrator on 2022/7/31.
//

#include "QuickHttp.h"

int so;
string buf;

//WinSocket初始化
void QuickHttp::initWinSocket() {
    WSADATA wsaData;
    //参数:版本号,指向WSADATA结构体的指针
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    so = socket(AF_INET, SOCK_STREAM, 0);
}

string QuickHttp::getBuf() {
    return buf;
}

int QuickHttp::httpGet(const string &strUrl, const string &strMethod, const string &strData) {
    //绑定地址端口
    int iPort = getPortFromUrl(strUrl);
    if (iPort < 0) {
        debugOut("获取URL端口失败\n");
        return -1;
    }
    string strIP = getIpFromUrl(strUrl);
    if (strIP.empty()) {
        debugOut("从URL获取IP地址失败\n");
        return -1;
    }
    //创建HTTP协议表头
    string strHttpHead = httpHeadCreate(strMethod, strUrl, strData);

    SOCKADDR_IN in;  //服务端地址
    in.sin_port = htons(iPort);//连接端口6000
    //绑定目标ip
    if (inet_pton(AF_INET, strIP.data(), &in.sin_addr) <= 0) {
        debugOut("inet_pton error ! Error code: %d,Error message:%s\n", errno, strerror(errno));
        return -1;
    }
    in.sin_family = AF_INET;
    //连接到服务端
    int isRet = connect(so, (SOCKADDR *) &in, sizeof(SOCKADDR));
    if (isRet == -1) {
        return -1;
    }
    //非阻塞方式连接
    send(so, strHttpHead.data(), strlen(strHttpHead.data()) , 0);
    /**
     * how的方式有三种分别是
     * SHUT_RD(0):关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。不允许接受。
     * SHUT_WR(1):关闭sockfd的写功能,此选项将不允许sockfd进行写操作。不允许发送。
     * SHUT_RDWR(2):关闭sockfd的读写功能。不允许发送和接受(和 close() 一样)。
     **/
    shutdown(so, 1);
    buf = httpDataTransmit(so);
    close(so);
    WSACleanup();
    return 1;
}

//发送HTTP请求并且接受响应
string QuickHttp::httpDataTransmit(int isSocFd) {
    //接受服务器返回的信息
    string info;//接受的信息
    char ch;//每次接受的信息
    int rlength = 0;//接受数据的总大小
    int rlen = recv(isSocFd, &ch, 1, 0);//每次接受的数据大小
    rlength += rlen;
    while (rlen != 0 && rlen != SOCKET_ERROR) {
        info += ch;
        rlen = recv(isSocFd, &ch, 1, 0);//每次接受的数据大小
        rlength += rlen;
    }

    //编码转换 防止在控制台显示乱码
    char *pszBuffer = new char[info.length() + 1];
    auto *pszWideBuffer = new wchar_t[(info.length() + 1) * 2];
    memset(pszWideBuffer, 0, (info.length() + 1) * 2);
    memset(pszBuffer, 0, info.length() + 1);
    int cchWideChar = info.length() + 1;
    MultiByteToWideChar(CP_UTF8, 0, info.c_str(), info.length(), pszWideBuffer, cchWideChar * 2);//将unicode编码,转换为宽字节
    WideCharToMultiByte(CP_ACP, 0, pszWideBuffer, wcslen(pszWideBuffer), pszBuffer, info.length() + 1, NULL,
                        NULL);//将宽字节,转换为控制台编码
    info = pszBuffer;
    delete[] pszBuffer;
    delete[] pszWideBuffer;
    return info;
}

QuickHttp *QuickHttp::getInstance() {
    return new QuickHttp();
}

//从HTTP请求URL中获取端口号
int QuickHttp::getPortFromUrl(const 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 QuickHttp::getIpFromUrl(const 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;
        }
    }

}

//从URL中获取Host地址
//从URL中获取Host地址
string QuickHttp::getHostAddFromUrl(const 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 QuickHttp::httpHeadCreate(const string &strMethod, const string &strUrl, const 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("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");

    return strHttpHead;
}

//从HTTP请求URL中获取HTTP请求参数
string QuickHttp::getParamFromUrl(const 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;
}

QuickBody *QuickHttp::getQuickBody() {
    QuickBody *quickBody = QuickBody::getInstance();
    quickBody->Analysis(getBuf());
    return quickBody;
}

void QuickHttp::debugOut(const string &fmt, ...) {
#ifdef __DEBUG__
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt.c_str(), ap);
    va_end(ap);
#endif
}

QuickHttp.h 头文件

//
// Created by Administrator on 2022/7/31.
//

#ifndef TRANSLATE_QUICKHTTP_H
#define TRANSLATE_QUICKHTTP_H

#include <winsock2.h>
#include "string"
#include "QuickBody.h"
#include <wspiapi.h>
#include <iostream>
#include <io.h>

#pragma comment(lib, "ws2_32.lib")
using namespace std;

#define BUFSIZE 4096
#define URLSIZE 2048

class QuickHttp {
public:
    //该只限制于win
    void initWinSocket();

    static QuickHttp *getInstance();

    int httpGet(const string &strUrl, const string &strMethod, const string &strData);

    string getBuf();

    QuickBody *getQuickBody();

private:
    string getIpFromUrl(const string &strUrl);

    int getPortFromUrl(const string &strUrl);

    string getHostAddFromUrl(const string &strUrl);

    void debugOut(const string &fmt, ...);

    string getParamFromUrl(const string &strUrl);

    string httpHeadCreate(const string &strMethod, const string &strUrl, const string &strData);

    string httpDataTransmit(int isSocFd);
};


#endif //TRANSLATE_QUICKHTTP_H

Body解析

QuickBody.cpp

//
// Created by Administrator on 2022/8/3.
//

#include <iostream>
#include "QuickBody.h"

string QuickBody::getCode() { return this->code; }

string QuickBody::getBody() { return this->body; }

map<string, string> QuickBody::getHeaders() {
    return this->headers;
}

QuickBody *QuickBody::getInstance() {
    return new QuickBody();
}

/**
 * http协议头文件开始解析
 * http协议内容分为三段:
 * 1.解析协议版本和状态码
 * 2。返回头内容
 * 3。内容
 * @param name 头文件
 */
void QuickBody::Analysis(const string &name) {
    vector<string> body = split(name, "\r\n\r\n");
    for (int i = 0; i < body.size(); i++) {
        if (i == 0) {
            initHeader(body[i]);
        } else {
            //内容
            this->body += body[i];
        }
    }
}

/**
 * 分割字符串
 * @param str 需要分割内容
 * @param pattern 分割特征码
 * @return vector<string>集合
 */
vector<string> QuickBody::split(const string &str1, const string &pattern) {
    string::size_type pos;
    vector<string> result;
    string str = str1 + pattern;//扩展字符串以方便操作
    int size = str.size();
    for (int i = 0; i < size; i++) {
        pos = str.find(pattern, i);
        if (pos < size) {
            string s = str.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }
    return result;
}

/**
 * 返回头解析
 * @param body
 */
void QuickBody::initHeader(string body) {
    vector<string> header = split(body, "\r\n");
    for (int j = 0; j < header.size(); ++j) {
        if (j == 0) {
            //状态码
            this->code = header[j];
        } else {
            //header获取key和value
            headersSplit(header[j], ": ");
        }
    }
}

void QuickBody::headersSplit(const string &str, const string &pattern) {
    string str1 = str + pattern;
    string::size_type pos;
    vector<string> result;
    int size = str1.size();
    for (int i = 0; i < size; i++) {
        pos = str1.find(pattern, i);
        if (pos < size) {
            string s = str1.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }
    this->headers.insert(map<string, string>::value_type(result[0], result[1]));
}

QuickBody.h

//
// Created by Administrator on 2022/8/3.
//

#ifndef TRANSLATE_QUICHBODY_H
#define TRANSLATE_QUICHBODY_H

#include "string"
#include <vector>
#include <utility>
#include <map>

using namespace std;

class QuickBody {
public:
    static QuickBody *getInstance();

    void Analysis(const string &name);

    vector<string> split(const string &str, const string &pattern);

    string getCode();

    string getBody();

    map<string, string> getHeaders();

private:
    map<string, string> headers;
    string code;
    string body;

    void initHeader(string body);

    void headersSplit(const string &str, const string &pattern);
};


#endif //TRANSLATE_QUICHBODY_H

main.cpp

//
//  main.cpp
//  HttpClient
//
//  Created by LiYong on 2018/1/23.
//

#include <iostream>
#include <vector>
#include "include/QuickHttp.h"

using namespace std;

int main(int argc, const char *argv[]) {
    QuickHttp *http = QuickHttp::getInstance();
    http->initWinSocket();
    http->httpGet("https://www.baidu.com", "GET", "");
    QuickBody *body = http->getQuickBody();
    cout << body->getBody();
    /*map<string, string> result = body->getHeaders();
    for (auto it = result.begin(); it != result.end(); it++) {
        cout << it->first << " " << it->second << endl;
    }*/
    return 0;
}

如果有大佬发现有问题签请多多指教

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值