创建一个httpserver、httpclient

最近因为要和java进行通信、约定好使用http协议进行消息传递。在网上找了很久server编写发现有个博主写的很详细,因此把东西记录下来以便下次使用。这是原博主网址:https://blog.csdn.net/h593245631/article/details/94033451

1、httpserver创建

1)httpserver.cpp

#include "httpserver.h"
#include <thread>
#include <regex>

#include "utils.h"
#include "TransactionApi.h"

using namespace std;

HttpServer::HttpServer() {
    //初始化winsock2.2相关的动态库
    WSADATA wd; // 获取socket相关信息
    if (0 != WSAStartup(MAKEWORD(2, 2), &wd)) { //0 表示成功
        cout << "WSAStartup error: " << WSAGetLastError() << endl;
        return;
    }
    cout << "WSAStartup success: " << endl;
}

HttpServer::~HttpServer() {
    //清理winsock2的环境
    WSACleanup();
}

bool HttpServer::start(unsigned short port) {
    _isExit = false;
    _port = port;
    if (!init()) {
        cout << "httpserver start error" << endl;
    }

    thread sth(&HttpServer::run, this);
    sth.detach();

    cout << "httpserver start success" << endl;
    return true;
}

void HttpServer::run() {
    //主线程循环接收客户端的链接
    while (!_isExit) {
        sockaddr_in addrClient;
        int len = sizeof(sockaddr_in);

        //4. 接收成功返回与client通讯的socket
        SOCKET c = accept(_listenSocket, (SOCKADDR*)&addrClient, &len);
        if (INVALID_SOCKET != c) {
            //创建线程 并且传入与client通讯的套接字
            thread sth(&HttpServer::clientThreadFun, this, (LPVOID)c);
            //thread sth(&HttpServer::clientThreadFun, this);
            sth.detach();
        }
    }
}

DWORD WINAPI HttpServer::clientThreadFun(LPVOID lpThreadParameter) {
    //5. 与客户端通讯 发送或者接收数据
    SOCKET c = (SOCKET)lpThreadParameter;

    //循环接收客户端数据
    int ret = 0;
    int cun =0;
    string connect;
    do {
        char buf[1024 * 2] = { 0 };

        ret = recv(c, buf, sizeof(buf) - 1, 0);

        if (ret <= 0) {
            break;
        }
        buf[ret] = '\0';
        cout << "客户端" << c << "请求信息 : " << buf << endl;

        if (!getRequest(buf)) {
            break;
        }
        
        
        //string respStr = getResponse(to_string(cun));
        string respStr = getResponse("testok");
        ret = send(c, respStr.c_str(), respStr.size(), 0);
        cout << "客户端" << c << "请求应答信息 : " << respStr << endl;
        break;

    } while (ret != SOCKET_ERROR && ret != 0);

    closesocket(c);
    return 0;
}


bool HttpServer::init() {

    //1. 创建TCP socket 流式套接字
    _listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS);
    if (INVALID_SOCKET == _listenSocket) {
        cout << "socket error :" << WSAGetLastError() << endl;
        return false;
    }

    //2. 绑定socket到一个IP地址和端口
    sockaddr_in addr; //不建议使用sockaddr 建议用sockaddr_in
    addr.sin_family = AF_INET; // 地址族
    addr.sin_port = htons(_port);//本地端口 转网络字节序
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);//ip地址转网络字节序

    int len = sizeof(sockaddr_in);
    if (SOCKET_ERROR == ::bind(_listenSocket, (sockaddr*)&addr, len)) {
        cout << "bind error: " << WSAGetLastError() << endl;
        return false;
    }

    //3. 监听, 5 代表正在等待完成相应的TCP三次握手过程的队列长度
    if (SOCKET_ERROR == listen(_listenSocket, 1000)) { // 根据电脑配置设定链接数
        cout << "listen error:" << WSAGetLastError() << endl;
        return false;
    }

    cout << "create listen success :" << endl;
    cout << "init htttpserver success :" << endl;
    return true;
}

void HttpServer::stop() {
    _isExit = true;
    //关闭监听套接字
    closesocket(_listenSocket);
}


//----

bool HttpServer::getRequest(string requestStr) {
    string src = requestStr;

    string pattern = "^([A-Z]+) /([a-zA-Z0-9]*([.][a-zA-Z]*)?)[?]?(.*) HTTP/1"; //
    regex r(pattern);
    smatch mas;
    regex_search(src, mas, r);
    if (mas.size() == 0) {
        cout << pattern.c_str() << " failed!" << endl;
        return false;
    }
    string type = mas[1];
    string path = "/";
    path += mas[2];  //根据path 区别请求的命令 比如 "/login" 登录   
    FunName =mas[2];
    string filetype = mas[3];
    string queryStr = mas[4];

    if (type != "GET" && type != "POST") {
        cout << "Not GET and POST!\n" << endl;
        return false;
    }

    //query id=1&name=xcj
    vector<string> querys;
    splitStr(queryStr, querys, "&");

    return true;
}

std::string HttpServer::getResponse(string connect) {
    //回应http GET请求
    //消息头
    string rmsg = "";
    rmsg = "HTTP/1.1 200 OK\r\n";
    rmsg += "Server: xHttp\r\n";
    rmsg += "Content-Type: text/html;charset=utf-8\r\n";
    rmsg += "Content-Length: ";
    rmsg += to_string(connect.size());
    //rmsg += connect;
    rmsg += "\r\n";
    rmsg += "\r\n\r\n";
    rmsg += connect;
    return rmsg;
}

2)httpserver.h

#pragma once
#pragma once

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>

using namespace std;

#pragma  comment(lib, "Ws2_32.lib")

class HttpServer {
public:
    HttpServer();
    ~HttpServer();

    bool start(unsigned short port);
    void run();
    void stop();
    string FunName;
private:
    bool init();
    //线程函数 处理客户端来的请求
    DWORD WINAPI clientThreadFun(LPVOID lpThreadParameter);

    bool _isExit = false;
    unsigned short _port = 8892;                    //端口号
    SOCKET _listenSocket = INVALID_SOCKET;        //监听套接字

    //处理http消息
    bool getRequest(string str);
    std::string getResponse(string connectLen);

};

3)utils.cpp

#include "utils.h"


void splitStr(const string& s, vector<string>& v, const string& c) {
    string::size_type pos1, pos2;
    pos2 = s.find(c);
    pos1 = 0;
    while (string::npos != pos2) {
        v.push_back(s.substr(pos1, pos2 - pos1));

        pos1 = pos2 + c.size();
        pos2 = s.find(c, pos1);
    }
    if (pos1 != s.length())
        v.push_back(s.substr(pos1));
}

4)utils.h

#include <vector>
#include <string>
#include <iostream>

using namespace std;

void splitStr(const string& s, vector<string>& v, const string& c);

5)main

#include "httpserver.h"
#include <WinSock.h>
#include <iostream>
#include <Windows.h>


int main(int argc, char* argv[]) {

    unsigned short port = 8888;
    HttpServer server;
    server.start(port);

    getchar();
}

本次程序是使用vs2019进行编译运行,配置的ip是127.0.0.1端口8888。

2、httpclient创建

1)httpclient.cpp

#include "httpclient.h"
#include <WinSock.h>
#include <iostream>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")


HttpRequest::HttpRequest(const std::string& ip, int port) : m_ip(ip), m_port(port)
{
}


HttpRequest::~HttpRequest(void)
{
}


// Http GET请求
std::string HttpRequest::HttpGet(std::string req)
{
    std::string ret = ""; // 返回Http Response
    try
    {
        // 开始进行socket初始化
        WSADATA wData;  
        ::WSAStartup(MAKEWORD(2, 2), &wData);  

        SOCKET clientSocket = socket(AF_INET, 1, 0);
        struct sockaddr_in ServerAddr = {0};
        ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        ServerAddr.sin_port = htons(8892);
        ServerAddr.sin_family = AF_INET; 
        int errNo = connect(clientSocket, (sockaddr*)&ServerAddr, sizeof(ServerAddr)); 
        if(errNo == 0)  
        {  
            //  "GET /[req] HTTP/1.1\r\n"  
            //  "Connection:Keep-Alive\r\n"
            //  "Accept-Encoding:gzip, deflate\r\n"  
            //  "Accept-Language:zh-CN,en,*\r\n"
            //  "User-Agent:Mozilla/5.0\r\n\r\n";
            std::string strSend = " HTTP/1.1\r\n"
                "Cookie:16888\r\n\r\n";
            strSend = "GET " + req + strSend;

            // 发送
            errNo = send(clientSocket, strSend.c_str(), strSend.length(), 0);
            if(errNo > 0)  
            {  
                //cout << "发送成功" << endl;
            }
            else
            {
                std::cout << "errNo:" << errNo << std::endl; 
                return ret;
            }

            // 接收
            char bufRecv[3069] = {0};
            errNo = recv(clientSocket, bufRecv, 3069, 0);
            if(errNo > 0)  
            {  
                ret = bufRecv;// 如果接收成功,则返回接收的数据内容  
            }
            else
            {
                std::cout << "errNo:" << errNo << std::endl; 
                return ret;
            }
        }  
        else  
        {  
            errNo = WSAGetLastError();  
            std::cout << "errNo:" << errNo << std::endl; 
        }  
        // socket环境清理
        ::WSACleanup();  
    }
    catch (...)
    {
        return "";
    }
    return ret;
}

// Http POST请求
std::string HttpRequest::HttpPost(std::string req, std::string data)
{
    std::string ret = ""; // 返回Http Response
    try
    {
        // 开始进行socket初始化;
        WSADATA wData;  
        ::WSAStartup(MAKEWORD(2, 2), &wData);  

        SOCKET clientSocket = socket(AF_INET, 1, 0);
        struct sockaddr_in ServerAddr = {0}; 
        ServerAddr.sin_addr.s_addr = inet_addr(m_ip.c_str());  
        ServerAddr.sin_port = htons(m_port);
        ServerAddr.sin_family = AF_INET; 
        int errNo = connect(clientSocket, (sockaddr*)&ServerAddr, sizeof(ServerAddr));  
        if(errNo == 0)  
        {   
            // 格式化data长度
            char len[10] = {0};
            sprintf(len, "%d", data.length());
            std::string strLen = len;

            //  "POST /[req] HTTP/1.1\r\n"
            //  "Connection:Keep-Alive\r\n"
            //  "Accept-Encoding:gzip, deflate\r\n"
            //  "Accept-Language:zh-CN,en,*\r\n"
            //  "Content-Length:[len]\r\n"
            //  "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n"
            //  "User-Agent:Mozilla/5.0\r\n\r\n"
            //  "[data]\r\n\r\n";
            std::string strSend = " HTTP/1.1\r\n"
                "Cookie:16888\r\n"
                "Content-Type:application/x-www-form-urlencoded\r\n"
                "Charset:utf-8\r\n"
                "Content-Length:";
            strSend = "POST " + req + strSend + strLen + "\r\n\r\n" + data;

            // 发送
            errNo = send(clientSocket, strSend.c_str(), strSend.length(), 0);
            if(errNo > 0)  
            {  
                //cout<<"发送成功\n";
            }
            else
            {
                std::cout << "errNo:" << errNo << std::endl; 
                return ret;
            }

            // 接收
            char bufRecv[3069] = {0};
            errNo = recv(clientSocket, bufRecv, 3069, 0);
            if(errNo > 0)  
            {  
                ret = bufRecv;// 如果接收成功,则返回接收的数据内容
            }
            else
            {
                std::cout << "errNo:" << errNo << std::endl; 
                return ret;
            }
        }  
        else  
        {  
            errNo = WSAGetLastError();  
        }  
        // socket环境清理
        ::WSACleanup();  
    }
    catch (...)
    {
        return "";
    }
    return ret;
}

// 合成JSON字符串
std::string HttpRequest::genJsonString(std::string key, int value)
{
    char buf[128] = {0};
    sprintf(buf, "{\"%s\":%d}", key.c_str(), value);
    std::string ret = buf;
    return ret;
}

// 分割字符串
std::vector<std::string> HttpRequest::split(const std::string &s, const std::string &seperator)
{
    std::vector<std::string> result;
    typedef std::string::size_type string_size;
    string_size i = 0;

    while(i != s.size()){
        // 找到字符串中首个不等于分隔符的字母
        int flag = 0;
        while(i != s.size() && flag == 0){
            flag = 1;
            for(string_size x = 0; x < seperator.size(); ++x)
                if(s[i] == seperator[x]){
                    ++i;
                    flag = 0;
                    break;
                }
        }

        // 找到又一个分隔符,将两个分隔符之间的字符串取出
        flag = 0;
        string_size j = i;
        while(j != s.size() && flag == 0){
            for(string_size x = 0; x < seperator.size(); ++x)
                if(s[j] == seperator[x]){
                    flag = 1;
                    break;
                }
                if(flag == 0) 
                    ++j;
        }
        if(i != j){
            result.push_back(s.substr(i, j-i));
            i = j;
        }
    }
    return result;
}

// 从Response中查找key对应的Header的内容
std::string HttpRequest::getHeader(std::string respose, std::string key)
{
    std::vector<std::string> lines = split(respose, "\r\n");
    for (int i = 0; i < lines.size(); i++)
    {
        std::vector<std::string> line = split(lines[i], ": ");// 注意空格
        if (line.size() >= 2 && line[0] == key)
        {
            return line[1];
        }
    }
    return "";
}

2)httpclient.h

#pragma once
#include <string>
#include <vector>

class HttpRequest
{
public:
    HttpRequest(const std::string& ip, int port);
    ~HttpRequest(void);

    // Http GET请求
    std::string HttpGet(std::string req);

    // Http POST请求
    std::string HttpPost(std::string req, std::string data);

    // 合成JSON字符串
    static std::string genJsonString(std::string key, int value);

    // 分割字符串
    static std::vector<std::string> split(const std::string &s, const std::string &seperator);

    // 根据key从Response获取Header中的内容
    static std::string getHeader(std::string respose, std::string key);

private:
    std::string         m_ip = "127.0.0.1";
    int             m_port=8892;
};

当ip127.0.0.1port8892服务器起好时将可以连接到服务器进行消息发送和接受应答结果。

 

posted @ 2022-03-06 13:15  枯藤呀~  阅读(177)  评论(0编辑  收藏  举报