C++ 实现HTTP的客户端、服务端demo和HTTP三方库介绍

news2024/10/7 10:17:34

        本文使用C++模拟实现http的客户端请求和http的服务端响应功能,并介绍几种封装HTTP协议的三方库。

1、实现简单HTTP的服务端功能

        本程序使用C++ tcp服务端代码模拟HTTP的服务端,服务端返回给客户端的消息内容按照HTTP协议的消息响应格式进行了组装。

demo如下:

#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    const char* hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\n\nHello, World!";

    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(37777);

    // 绑定套接字到端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Listening on port 37777\n");
    while (true) {
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        // 读取客户端请求
        int length = read(new_socket, buffer, 1024);
        printf("******** HTTP Server recv request length: %d,as follow: ********\n%s\n",length,buffer);

        // 发送响应
        send(new_socket, hello, strlen(hello), 0);
        printf("******** HTTP Server replied to the request ********\n");
        close(new_socket);
    }

    return 0;
}

2、实现简单HTTP的客户端功能

         本程序使用C++ tcp客户端代码模拟HTTP的客户端,客户端发给服务端的请求内容按照HTTP协议的请求格式进行了组装。

demo如下:

#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int sock = 0, length;
    struct sockaddr_in serv_addr;
    char buffer[1024] = {0};
    const char* get_request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation error");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(37777);

    //serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //bind ip method 1
    // 将IPv4地址从点分十进制转换为二进制形式 bind ip method 2
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("Invalid address/Address not supported");
        exit(EXIT_FAILURE);
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }

    send(sock, get_request, strlen(get_request), 0);
    printf("HTTP Client GET method has been sent\n");

    length = read(sock, buffer, 1024);
    printf("******** HTTP Client receive msg length: %d,context as follow: ********\n%s\n",length,buffer);
    printf("****************\n");
    close(sock);
    return 0;
}

分别运行上面的服务端和客户端程序,服务端程序运行结果如下:

客户端程序运行结果如下:

 3、实现HTTP的客户端访问百度功能

        本程序使用C++ tcp客户端代码模拟HTTP的客户端,按照http协议请求格式对百度网页进行GET请求消息的组装。

实现demo如下:

#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>


#define URL  "/"                            //资源
#define HTTP_VESTION "HTTP/1.1"
#define HOST "www.baidu.com"                //域名
#define	CONNETION  "Connection: close\r\n"  //短链接
#define BUFFER_SIZE		1024
std::string strBuffer = "";
int getIpAddress(char** ip)
{
    int ret = -1;
    struct  hostent *ht = NULL;
    ht = gethostbyname(HOST);
    if(ht) {
        printf("type:%s\n", ht->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6");
        for(int i=0; ;i++) {
            if(ht->h_addr_list[i]!=NULL) {
                *ip = inet_ntoa(*((struct in_addr *)ht->h_addr_list[i]));
                ret = 1;
                break;
            }
        }
    }
    return ret;
}


char* receiveNew(int sockfd, size_t& len) {
    char* buffer = nullptr; // 初始化为nullptr
    ssize_t totalBytesReceived = 0; // 累计接收的字节数
    ssize_t bytesReceived; // 当前接收的字节数

    // 循环接收数据,直到对方关闭连接或发生错误
    do {
        // 分配额外的空间以容纳更多数据
        char* newBuffer = new char[totalBytesReceived + 1024]; // 假设每次额外分配1024字节,用于接受下次数据
        if (buffer) {
            // 如果有旧的数据,复制到新的缓冲区,每次所有内容复制一遍
            std::memcpy(newBuffer, buffer, totalBytesReceived);
            delete[] buffer; // 释放旧缓冲区
        }
        buffer = newBuffer; // 更新指针以指向新的缓冲区,指向的内容可以任意大,即只要可分配

        // 接收数据
        bytesReceived = recv(sockfd, buffer + totalBytesReceived, 1024, 0);

        if (bytesReceived == -1) {
            // 错误处理,例如检查errno
            std::cerr << "Error receiving data: " << strerror(errno) << std::endl;
            delete[] buffer; // 释放已分配的缓冲区
            return nullptr; // 返回nullptr表示错误
        }

        totalBytesReceived += bytesReceived; // 累计已接收的字节数

        // 如果需要,可以在此处设置接收的最大字节数限制
    } while (bytesReceived > 0); // 当recv返回0时,表示对方已关闭连接

    // 调整缓冲区的大小以匹配实际接收的数据量
    char* finalBuffer = new char[totalBytesReceived + 1]; // +1为了字符串终止符'\0'
    std::memcpy(finalBuffer, buffer, totalBytesReceived);
    finalBuffer[totalBytesReceived] = '\0'; // 添加字符串终止符
    delete[] buffer; // 释放中间缓冲区

    len = totalBytesReceived; // 更新len以反映实际接收的数据长度
    return finalBuffer; // 返回最终缓冲区
}

void receiveString(int sockfd, size_t& len) {
    char buffer[BUFFER_SIZE] = {}; // 初始化为nullptr
    ssize_t totalBytesReceived = 0; // 累计接收的字节数
    ssize_t bytesReceived; // 当前接收的字节数

    // 循环接收数据,直到对方关闭连接或发生错误
    do {
          // 接收数据
        bytesReceived = recv(sockfd, buffer, BUFFER_SIZE, 0);
        if (bytesReceived == -1) {
            // 错误处理,例如检查errno
            printf("Error receiving data:%s", strerror(errno));
        }
        strBuffer += buffer;
        totalBytesReceived += bytesReceived; // 累计已接收的字节数
        memset(buffer,0,BUFFER_SIZE);
        // 如果需要,可以在此处设置接收的最大字节数限制
    } while (bytesReceived > 0); // 当recv返回0时,表示对方已关闭连接

    len = totalBytesReceived;
}

void MakeSocketConnect()
{
    char* ipAddr = nullptr;
    getIpAddress(&ipAddr);
    int sock = 0;
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(80);
    if (inet_pton(AF_INET, ipAddr, &serv_addr.sin_addr) <= 0) {
        perror("Invalid address/Address not supported");
        exit(EXIT_FAILURE);
    }

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation error");
        exit(EXIT_FAILURE);
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }

    char get_request[1024] = {};
    sprintf(get_request,"GET %s %s\r\n"
                        "Host: %s\r\n"
                        "%s\r\n"
                        "\r\n",
            URL,HTTP_VESTION,HOST,CONNETION);


    send(sock, get_request, strlen(get_request), 0);
    printf("HTTP Client GET method has been sent\n");
    size_t len = 0; // 用于保存接收到的数据的长度

    /* 由于HTTP服务端回复的消息客户端一次接收不完,需要多次接收,直到数据接收完毕
    *  本文采用下面两种C++方法进行接收
    *  方法1:采用new方法分配内存,缺点:需要多次分配,并且需要多次memcpy(可以使用c语言malloc分配内存,realloc扩容)
    *  方法2:采用string容器(stinrg是字符串容器),动态的存储数据,也可以采用vector<char>容器存储
    */
#if 0
    char* receivedData = receiveNew(sock,len);
    if (receivedData) {
            // 处理接收到的数据
            printf("******** HTTP Client receive msg length: %d,context as follow: ********\n%s\n",len,receivedData);
            printf("****************\n");
            // 释放动态分配的内存
            delete[] receivedData;
        } else {
            // 错误处理
            printf("Failed to receive data.\n");
        }
#else
    receiveString(sock,len);
    printf("******** HTTP Client receive msg length: %d,context as follow: ********\n%s\n",len,strBuffer.c_str());
    printf("****************\n");
#endif


    close(sock);
}

int main() {

    MakeSocketConnect();

    return 0;
}

上面程序运行结果如下:

上面接收的响应正文太长,上面图片没有截完。

C++ 实现http请求用法也可参考下面的文章:Linux C/C++ 实现HTTP请求器(TCP客户端)_linux c++ http-CSDN博客

4、HTTP协议的三方库

        使用封装HTTP协议的三方库,可以高效开发HTTP的功能需求,HTTP的协议封装有很多的三方库,下面只介绍几种,更多的三方库可查看其他资料。

(1)cpp-httplib

       优点:

                轻量级:非常轻量,仅包含一个头文件,方便集成到项目中。

                简单易用:API设计直观,可以快速上手。

                跨平台:支持多种操作系统,如Windows、Linux和macOS等。

                支持断点续传:通过ETag和Range字段,可以实现断点续传功能。

        缺点:

                功能相对简单:相比于其他HTTP库,可能不支持一些高级特性。

(2)libcurl

         优点:

                功能强大:支持多种协议,包括HTTP、HTTPS、FTP等。

                灵活性强:支持代理、HTTP POST、SSL连接、HTTP PUT等多种功能。

                跨平台:可以在多种操作系统上运行。

                与其他库配合:可以与其他网络库如libevent、openssl等配合使用,实现高性能的网络

                 编程。

         缺点:

                依赖外部库:需要安装和配置libcurl库。

        libcurl的介绍和使用可参考libcurl开源库的编译与使用全攻略_libcurl编译-CSDN博客

(3)Poco C++ Libraries

         优点:

                功能丰富:除了HTTP功能外,还包含其他实用程序库,如日志记录、XML解析等。

                跨平台:支持多种操作系统和编译器。

                稳定性:经过长时间的验证和广泛的使用,具有较高的稳定性。

         缺点:

                较为庞大:包含多个库和模块,可能对于只需要HTTP功能的项目来说过于庞大。

        cpp-httplib适合需要轻量级、简单易用且跨平台的HTTP库的项目。 libcurl适合需要强大功能和灵活性的项目,特别是需要支持多种协议和与其他库配合使用的场景。Poco C++ Libraries适合需要丰富功能和稳定性的大型项目。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1817118.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云 BI 数据分析与可视化的快速入门指南

前言 腾讯云 BI 是一款商业智能解决方案&#xff0c;提供数据接入、分析、可视化、门户搭建和权限管理等全流程服务。它支持敏捷自助设计&#xff0c;简化报表制作&#xff0c;并通过企业微信等渠道实现协作。产品分为个人版、基础版、专业版和私有化版&#xff0c;满足不同规…

联想电脑 调节屏幕亮度不起使用,按F5,F6,屏幕上的hotkeys进度条是在改变,但是屏幕没有一些作用的处理方法

1、查看驱动是否正常 Win键X &#xff0c;设备管理器 发现似乎挺正常的。 查看原厂驱动&#xff1a;联想电脑管家 这样看来&#xff0c;驱动是没有问题了。 2、看看设置电池模式 其实还是这个电池模式的问题导致。 如果处于养护模式的话&#xff0c;充电只在75%~80%&#x…

探索Edge

目录 1.概述 1.1.什么是浏览器 1.2.浏览器的作用 2.Edge 2.1.什么是Edge 2.2.诞生背景 2.3.历史版本 2.4.作用 2.5.优缺点 2.5.1.优点 2.5.2.缺点 3.对比 3.1.和360浏览器的对比 3.2.和谷歌浏览器&#xff08;Chrome&#xff09;的对比 4.未来展望 5.总结 1.概…

浏览器f12控制台怎么获取vue实例,并且修改data数据

我们在日常的生产工作中&#xff0c;经常会遇到一些问题&#xff0c;比如&#xff0c;若产品已经部署&#xff0c;或是目前无法查看源代码&#xff0c;或者向用命令直接修改查询默认表单数据&#xff0c;那我们怎么去查看Vue实例呢&#xff1f; 我们在浏览器直接打印this不能得…

[Alogithm][动态规划][背包问题][组合总和IV][不同的二叉搜索树]详细讲解

目录 1.组合总和 Ⅳ1.题目链接2.算法原理详解3.代码实现 2.不同的二叉搜索树1.题目链接2.算法原理详解3.代码实现 1.组合总和 Ⅳ 1.题目链接 组合总和 Ⅳ 2.算法原理详解 本题是个排列题&#xff0c;而并非组合题&#xff0c;所以并非背包问题 思路&#xff1a; 确定状态表示…

【spring 】支持spring WebFlux 的容器

spring WebFlux 是 Spring 5 引入的响应式 Web 框架&#xff0c;它支持非阻塞、事件驱动的编程模型&#xff0c;特别适合处理高并发的场景。 Spring WebFlux 可以运行在多种容器上 包括下面&#xff1a; Netty: Netty 是一个异步事件驱动的网络应用程序框架&#xff0c;用于快…

OpenCV 的模板匹配

OpenCV中的模板匹配 模板匹配&#xff08;Template Matching&#xff09;是计算机视觉中的一种技术&#xff0c;用于在大图像中找到与小图像&#xff08;模板&#xff09;相匹配的部分。OpenCV提供了多种模板匹配的方法&#xff0c;主要包括基于相关性和基于平方差的匹配方法。…

【RPC项目-1】0612

写在前面&#xff0c;主要是作学习回顾笔记&#xff0c;以备后续面试 LogEvent 组成&#xff1a; 日志级别&#xff08;level&#xff09;日期时间(精确到ms)进程号pid&#xff0c;线程号threadid文件名file_name、行号line自定义msg 输出格式如&#xff1a;[level]\t[%y-%…

dvadmin 调试问题

链接&#xff1a;django-vue3-admin: django-vue3-admin 是一套全部开源的快速开发平台&#xff0c;毫无保留给个人免费使用、团体授权使用。 django-vue3-admin 基于RBAC模型的权限控制的一整套基础开发平台&#xff0c;权限粒度达到列级别&#xff0c;前后端分离&#xff0c;…

vb.net小demo(计算器、文件处理等/C#也可看)

Demo1&#xff1a;使用窗体控件实现一个简易版计算器 Public Class Form1Private Sub Button_1_Click(sender As Object, e As EventArgs) Handles Button_1.ClickCalSubBox.Text Button_1.TextEnd SubPrivate Sub Button_2_Click(sender As Object, e As EventArgs) Handles …

HBase数据存储

1、数据模型 Namespace&#xff08;表命名空间&#xff09;&#xff1a;表命名空间不是强制的&#xff0c;当想把多个表分到一个组去统一管理的时候才会用到表命名空间。Table&#xff08;表&#xff09;&#xff1a;一个表由一个或者多个列族组成。数据属性&#xff0c;都在列…

javaWeb项目-ssm+vue个人博客网站管理系统功能介绍

本项目源码&#xff1a;java-基于ssmvue的个人博客网站的设计与实现源码说明文档资料资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a…

第 5 章:面向生产的 Spring Boot

在 4.1.2 节中&#xff0c;我们介绍了 Spring Boot 的四大核心组成部分&#xff0c;第 4 章主要介绍了其中的起步依赖与自动配置&#xff0c;本章将重点介绍 Spring Boot Actuator&#xff0c;包括如何通过 Actuator 提供的各种端点&#xff08;endpoint&#xff09;了解系统的…

DDD领域应用理论实践分析回顾

目录 一、DDD的重要性 &#xff08;一&#xff09;拥抱互联网黑话&#xff08;抓痛点、谈愿景、搞方法论&#xff09; &#xff08;二&#xff09;DDD真的重要吗&#xff1f; 二、领域驱动设计DDD在B端营销系统的实践 &#xff08;一&#xff09;设计落地步骤 &#xff0…

小程序无法调用服务端问题排查

1、问题描述 突然有一天线上的小程序不能登录&#xff0c;经查小程序无法调用。经查无法小程序页面无法调用后台服务。 2、排查过程 由于无法登录小程序发布服务器&#xff0c;无法测试小程序前端服务器到服务端网络&#xff0c;并且小程序无法看到日志。所以就得从服务端和网…

使用CSS、JavaScript、jQuery三种方式实现手风琴效果

手风琴效果有不少&#xff0c;王者荣耀官网&#xff08;源网址 https://pvp.qq.com/raiders/ &#xff09;有一处周免英雄&#xff0c;使用的就是手风琴效果&#xff0c;如图所示。 我试着用css、js、jQuery三种方式实现了这种效果&#xff0c;最终效果差不多&#xff0c;美中不…

IDEA创建简单web(servlet)项目(server为tomcat)

引言 鉴于网上很少有关于IDEA开发servlet项目的教程&#xff08;24版idea&#xff0c;并且servlet技术十分复古&#xff0c;很少有人用到&#xff0c;能够理解&#xff0c;该文章旨在为在校的学生提供一个参考&#xff0c;项目技术简单&#xff09;本人在此总结从头开始到项目…

MATLAB算法实战应用案例精讲-【数模应用】有序Logit(Logistic)分析

目录 算法原理 logit回归分析步骤 二元logit分析 多分类logit分析 有序logit分析 总结 SPSS 1、问题与数据 2、对数据结构的分析 3、SPSS分析方法 SPSSAU 有序Logit回归案例 1、背景 2、理论 3、操作 4、SPSSAU输出结果 5、文字分析 6、剖析 疑难解惑 提示…

Joplin Typora 粘贴图片 | 当使用Typora作为Joplin编辑器时,如何粘贴图片并上传到Joplin服务器,替换链接

一、背景 当我们使用Joplin时&#xff0c;上传图片时会自动上传到Joplin服务器并替换链接 但是Joplin的编辑器不好用&#xff0c;我更习惯用Typora来编辑&#xff0c; 然而Typora中上传的图片只能在本地&#xff0c;无法上传到Joplin服务器&#xff0c;在其他客户端也看不到图片…

迈向『闭环』| PlanAgent:基于MLLM的自动驾驶闭环规划新SOTA!

中科院自动化所深度强化学习团队联合理想汽车等提出了一种新的基于多模态大语言模型MLLM的自动驾驶闭环规划框架—PlanAgent。该方法以场景的鸟瞰图和基于图的文本提示为输入&#xff0c;利用多模态大语言模型的多模态理解和常识推理能力&#xff0c;进行从场景理解到横向和纵向…