C#与C++交互开发系列(二十二):跨进程通信之使用基于HTTP协议的REST风格的API

在这里插入图片描述

1. 前言

REST API(Representational State Transfer Application Programming Interface)是一种基于HTTP协议的通信方式,广泛用于网络服务和分布式应用程序之间的通信。通过REST API,可以让C#和C++应用程序进行跨进程、甚至跨平台的通信。本文将详细讲解如何使用REST API实现C#和C++程序之间的通信,并附上可真实调试的代码示例。

2. REST API简介

在这里插入图片描述
REST是一种架构风格,利用HTTP协议中的GET、POST、PUT、DELETE等方法与服务器交互,通常用于客户端和服务器之间的数据交换。REST API通信的核心在于定义资源,客户端通过URL访问服务器上的资源,并通过HTTP方法操作这些资源。

3. C++构建服务端和C#构建客户端实现双向交互

1. C++构建Restful服务端

Boost 是一个广受欢迎的 C++ 开源库集合,提供了诸多用于跨平台开发的实用工具。Boost 涵盖了从算法到 I/O、正则表达式、容器和多线程等多种功能,尤其适合构建高性能的服务器、网络和系统应用。我们就使用 Boost库构建一个 RESTful 风格的 C++ Web API。

1.1 Boost库的核心依赖

在这里插入图片描述

我们主要使用两个核心库:

  1. Boost.Asio:提供跨平台的异步 I/O 功能,适用于构建高效的 TCP、UDP 等网络应用。
  2. Boost.Beast:基于 Boost.Asio 实现的 HTTP 和 WebSocket 协议库,简化了 RESTful API 的构建。

通过vcpkg命令安装 Boost.Asio库

vcpkg install boost-asio

在这里插入图片描述
通过vcpkg命令安装 Boost.Beast库

vcpkg install boost-beast

在这里插入图片描述

1.2 构建 RESTful C++ Web API

使用 Boost.Beast 构建一个简单的 RESTful API 服务,支持基本的 CRUD(创建、读取、更新、删除)操作。我们的 API 将运行在端口 8080 上,支持以下路径:

  • GET /api/data:获取数据列表
  • POST /api/data:创建新数据
  • PUT /api/data:更新数据
  • DELETE /api/data:删除数据
1.3 编码实现服务端代码
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <iostream>
#include <string>
#include <thread>

namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = asio::ip::tcp;

std::string data_store = "Sample data";  // 简单的数据存储

// 处理 HTTP 请求的函数
void handle_request(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
    if (req.method() == http::verb::get && req.target() == "/api/data") {
        res.result(http::status::ok);
        res.set(http::field::content_type, "application/json");
        res.body() = R"({"data": ")" + data_store + R"("})";
    }
    else if (req.method() == http::verb::post && req.target() == "/api/data") {
        data_store = req.body();
        res.result(http::status::created);
        res.set(http::field::content_type, "application/json");
        res.body() = R"({"message": "Data created"})";
    }
    else if (req.method() == http::verb::put && req.target() == "/api/data") {
        data_store = req.body();
        res.result(http::status::ok);
        res.set(http::field::content_type, "application/json");
        res.body() = R"({"message": "Data updated"})";
    }
    else if (req.method() == http::verb::delete_ && req.target() == "/api/data") {
        data_store.clear();
        res.result(http::status::ok);
        res.set(http::field::content_type, "application/json");
        res.body() = R"({"message": "Data deleted"})";
    }
    else {
        res.result(http::status::not_found);
        res.set(http::field::content_type, "application/json");
        res.body() = R"({"error": "Resource not found"})";
    }
    res.prepare_payload();
}

// 会话处理
void session(tcp::socket socket) {
    beast::flat_buffer buffer;
    http::request<http::string_body> req;
    http::response<http::string_body> res;

    try {
        http::read(socket, buffer, req);
        handle_request(req, res);
        http::write(socket, res);
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

// 主程序启动服务器
int main() {
    try {
        asio::io_context ioc;
        tcp::acceptor acceptor(ioc, tcp::endpoint(tcp::v4(), 8080));
        std::cout << "Server running on http://localhost:8080\n";

        while (true) {
            tcp::socket socket(ioc);
            acceptor.accept(socket);
            std::thread(&session, std::move(socket)).detach();
        }
    } catch (const std::exception& e) {
        std::cerr << "Server error: " << e.what() << std::endl;
    }
    return 0;
}
1.4 启动服务程序

在这里插入图片描述
访问地址:http://localhost:8080/
在这里插入图片描述
访问地址:http://localhost:8080/api/data
在这里插入图片描述

2. 构建C#客户端调用C++ RESTful API

使用 C# 的 HttpClient 调用 C++ RESTful API 并进行数据交互。

2.1 编码实现客户端代码
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace CppWebApiClient
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();

        static async Task Main(string[] args)
        {
            // GET 请求
            HttpResponseMessage response = await client.GetAsync("http://localhost:8080/api/data");
            string getData = await response.Content.ReadAsStringAsync();
            Console.WriteLine("GET Response: " + getData);

            // POST 请求
            string newData = "\"New sample data\"";
            response = await client.PostAsync("http://localhost:8080/api/data", new StringContent(newData, Encoding.UTF8, "application/json"));
            Console.WriteLine("POST Response: " + await response.Content.ReadAsStringAsync());

            // PUT 请求
            string updateData = "\"Updated data\"";
            response = await client.PutAsync("http://localhost:8080/api/data", new StringContent(updateData, Encoding.UTF8, "application/json"));
            Console.WriteLine("PUT Response: " + await response.Content.ReadAsStringAsync());

            // DELETE 请求
            response = await client.DeleteAsync("http://localhost:8080/api/data");
            Console.WriteLine("DELETE Response: " + await response.Content.ReadAsStringAsync());

            Console.ReadKey();
        }
    }
}

启动客户端程序
在这里插入图片描述
访问地址:http://localhost:8080/api/data
在这里插入图片描述
数据已被删除。

4. C++构建客户端和C#构建服务端实现双向交互

我们将通过两个部分来构建一个RESTful风格的API服务(使用ASP.NET Core WebAPI)以及如何在C++中调用这个WebAPI。

1. 构建ASP.NET Core WebAPI服务

  1. 创建ASP.NET Core WebAPI项目

    • 打开命令行或Visual Studio。
    • 创建一个新的ASP.NET Core WebAPI项目:
      dotnet new webapi -n MyApi
      cd MyApi
      
  2. 定义一个简单的Controller

    我们创建一个简单的Controller ProductsController.cs,提供几个GET和POST的接口

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using System.Collections.Generic;	
    namespace MyApi.Controllers
    {
        public class ProductRequest
        {
            public string Product { get; set; }
        }	
        [Route("api/[controller]")]
        [ApiController]
        public class ProductsController : ControllerBase
        {
            // 这是一个存储产品的示例
            private static readonly List<string> Products = new List<string>
            {
                "Product1",
                "Product2",
                "Product3"
            };	
            // 获取所有产品
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return Ok(Products);
            }	
            // 获取单个产品
            [HttpGet("{id}")]
            public ActionResult<string> Get(int id)
            {
                if (id < 0 || id >= Products.Count)
                {
                    return NotFound();
                }
                return Ok(Products[id]);
            }	
            // 创建新产品
            [HttpPost]
            public ActionResult Post([FromBody] ProductRequest product)
            {
                if (string.IsNullOrEmpty(product?.Product))
                {
                    return BadRequest("Product cannot be empty");
                }
    
                Products.Add(product.Product);
                return CreatedAtAction(nameof(Get), new { id = Products.Count - 1 }, product);
            }
        }
    }
    
  3. 运行API
    在命令行中,运行以下命令启动API服务器:

    dotnet run
    

在这里插入图片描述

2. 构建C++客户端调用WebAPI

现在,我们已经创建了一个基本的WebAPI服务。接下来,我们将在C++中编写代码,通过HTTP请求来调用这个WebAPI。为了发送HTTP请求,C++没有内置的库,因此我们将使用一些第三方库,如libcurl

  1. 安装libcurl库

    可以从libcurl官网下载并安装。
    或者通过vcpkg安装
    vcpkg install curl 在这里插入图片描述

  2. C++代码:使用libcurl调用WebAPI

    在C++中,我们将使用libcurl库来发送HTTP请求。

    #include <iostream>
    #include <string>
    #include <curl/curl.h>	
    // 回调函数,用于处理HTTP响应数据
    size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
        size_t totalSize = size * nmemb;
        ((std::string*)userp)->append((char*)contents, totalSize);
        return totalSize;
    }	
    // GET请求
    void GetProducts() {
        CURL* curl;
        CURLcode res;
        std::string readBuffer;	
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl = curl_easy_init();	
        if (curl) {
            curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:5056/api/products");
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);	
            // 执行请求
            res = curl_easy_perform(curl);	
            // 检查请求是否成功
            if (res != CURLE_OK) {
                std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
            }
            else {
                std::cout << "Response: " << readBuffer << std::endl;
            }	
            curl_easy_cleanup(curl);
        }	
        curl_global_cleanup();
    }
    
    // POST请求
    void AddProduct(const std::string& product) {
        CURL* curl;
        CURLcode res;
        std::string readBuffer;	
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl = curl_easy_init();	
        if (curl) {
            // 设置URL
            curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:5056/api/products");	
            // 设置HTTP头
            struct curl_slist* headers = NULL;
            headers = curl_slist_append(headers, "Content-Type: application/json");	
            // 设置POST数据
            std::string jsonData = "{\"product\":\"" + product + "\"}";	
            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());	
            // 处理响应
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);	
            // 执行请求
            res = curl_easy_perform(curl);
            // 检查请求是否成功
            if (res != CURLE_OK) {
                std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
            }
            else {
                std::cout << "Response: " << readBuffer << std::endl;
            }
            curl_easy_cleanup(curl);
            curl_slist_free_all(headers);
        }
        curl_global_cleanup();
    }	
    int main() {
        // 获取所有产品
        GetProducts();
        // 添加新产品
        AddProduct("Product4");
        // 获取所有产品,查看是否添加成功
        GetProducts();
        int i;
        std::cin >> i;
        return 0;
    }
    
  3. 编译并运行C++代码 在这里插入图片描述

5. 注意事项

  1. 安全性:在生产环境中,请使用SSL证书,确保API通信的安全性。在开发环境中可以临时禁用SSL验证。
  2. 数据格式:通常REST API使用JSON格式传递数据,便于解析。
  3. 错误处理:在生产中,要添加错误处理,捕获网络问题和API错误状态码,确保程序稳定性。

6. 应用场景

  • 跨平台通信:REST API允许不同编程语言和平台的程序通过HTTP协议进行通信,适用于需要跨平台数据交互的项目。
  • 网络服务:REST API广泛用于微服务架构,可实现松耦合的服务部署和集成。

7. 优缺点

  • 优点
    • 标准化的HTTP协议,支持跨平台通信。
    • 支持不同的数据格式,如JSON和XML,数据处理灵活。
  • 缺点
    • 通信效率受限于网络性能,特别是在传输大量数据时可能带来延迟。
    • 使用HTTP请求可能比本地IPC方法(如命名管道、共享内存等)稍慢。

8. 总结

REST API 提供了一种灵活且标准化的通信方式,能够在不同语言和平台间实现通信。通过本文的示例代码,C++ 和 C# 程序可以借助 REST API 实现数据交互。这种方法尤其适合分布式应用和微服务架构的设计。
在下一篇中,我们将介绍gRPC,这是一个高效的跨平台通信协议,非常适合高性能需求的场景。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ECharts饼图-饼图15,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个饼图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供详…

Python在数据科学中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Python在数据科学中的应用 Python在数据科学中的应用 Python在数据科学中的应用 引言 Python 概述 定义与特点 发展历程 Python…

IDEA2024:右下角显示内存

使用场景&#xff1a; 实时知晓idea内存使用情况 解决方案: 开启内存显示 View -> Apperance -> Status Bar Widgets -> Memory Indicator 效果如下&#xff1a;

【计算机网络】【网络层】【习题】

计算机网络-传输层-习题 文章目录 13. 图 4-69 给出了距离-向量协议工作过程&#xff0c;表&#xff08;a&#xff09;是路由表 R1 初始的路由表&#xff0c;表&#xff08;b&#xff09;是相邻路由器 R2 传送来的路由表。请写出 R1 更新后的路由表&#xff08;c&#xff09;。…

vue 计算属性get set

<template><div id"app"><h1>用户信息</h1><p>全名&#xff1a;{{ fullName }}</p><input v-model"fullName" placeholder"请输入全名" /><p>姓&#xff1a;{{ firstName }}</p><p>…

74HC245

74HC245&#xff1a;典型的CMOS型缓冲门电路 在这里用于增加电压

【代码管理之道】Git 高级工作流与团队协作实践:深入探讨与实战案例

引言 在前几篇文章中&#xff0c;我们详细介绍了 Git 的基本概念、高级功能、最佳实践以及高级工作流和团队协作实践。本文将继续深入探讨 Git 的高级工作流和团队协作实践&#xff0c;帮助读者更好地理解和应用这些概念。我们将通过具体的实战案例&#xff0c;展示如何在实际…

NopReport中如何通过可扩展性设计实现二维码导出

NopReport是从零开始编写的下一代中国式报表引擎&#xff0c;它的核心仅有3000多行代码&#xff0c;但是完整实现了中国式非线性报表理论所定义的层次坐标和行列对称展开算法。 使用介绍&#xff1a;采用Excel作为设计器的开源中国式报表引擎:NopReport, 视频讲解源码分析: 非…

Linux(光速安装+rocky linux镜像)

寻找镜像 Download - Rocky Linux 如果用作桌面的&#xff0c;下载DVD的选项&#xff0c;占的存储比较多了&#xff0c;如果下载最小的&#xff0c;则没有桌面环境。 配置虚拟机 Linux&#xff08;光速安装centos镜像 图片大白话&#xff09;-CSDN博客 有些一样的我就不一…

python文件命名,不注意容易出错

在python中&#xff0c;文件名也会作为模块的名称使用。 举个例子 工程目录如下&#xff1a; 其中&#xff0c;文件夹为sys_check&#xff0c;其下还有一个sys_check1.py文件。 如果该文件名也是sys_check.py&#xff0c;可能会导致问题&#xff0c;在其它文件中引用模块时…

给阿里云OSS启用SSL

自定义域名需要指向阿里云 OSS&#xff0c;并且你希望为这个域名获取 SSL 证书&#xff0c;可以使用 DNS 验证的方法来获取证书。以下是详细步骤&#xff1a; 关键前提&#xff1a; 关键是需要在阿里云控制台的域名 权威域名解析中添加子域名aliyuncs.xxx.com 使用 DNS 验证获取…

边缘计算在智能制造中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 边缘计算在智能制造中的应用 边缘计算在智能制造中的应用 边缘计算在智能制造中的应用 引言 边缘计算概述 定义与原理 发展历程 …

定时任务进行简单监控、爬虫的自动化之旅

原文链接&#xff1a;「定时任务」进阶指南&#xff1a;监控、爬虫的自动化之旅

『VUE』25. 组件事件与v-model(详细图文注释)

目录 功能介绍示例总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 功能介绍 预期拿到一个输入搜索框,用户在搜索框中输入数据后实时把数据发送给父组件使用. 示例 主要是对前面的v-model和watch的结合使用,实现获取更新的子…

【Python TensorFlow】进阶指南(续篇二)

在前面的文章中&#xff0c;我们详细探讨了TensorFlow在实际应用中的高级功能和技术细节。本篇将继续深入探讨一些前沿话题&#xff0c;包括但不限于分布式训练、混合精度训练、神经架构搜索&#xff08;NAS&#xff09;、模型微调以及在实际项目中的最佳实践等&#xff0c;帮助…

什么是MVC模式?

MVC 模型处理数据 控制器做传递 视图用于展示 模型Model:数据验证、逻辑和持久性&#xff0c;直接与数据库进行交互控制器Controller&#xff1a;向模型询问数据&#xff0c;获取所有的数据信息视图View&#xff1a;视图用于显示信息&#xff0c;根据模型来获取信息&#xff0c…

Spring Boot 接口防重复提交解决方案

文章目录 前言使用Token机制实现步骤1.生成Token2.传递Token3.验证Token 使用Redis实现步骤1.引入Redis依赖2.生成Token3.传递Token4.验证Token 使用Spring AOP实现步骤1.定义注解2.创建切面3.使用注解 总结 前言 在Web开发中&#xff0c;防止用户重复提交表单是一个常见的需求…

【毫米波雷达(九)】前雷达软件开发遇到的问题汇总及解决方法

前雷达软件开发遇到的问题汇总及解决方法 一、CAN/CANFD通信1、雷达CAN未能正常发出数据2、雷达在车上接收不到车身信息3、程序下载失败4、DV试验发送数据偶发断连5、发送感知信息丢帧或者丢报文6、上电发出第一帧的报文时间长7、ZCANPRO有错误帧二、协议转换&#xff08;以太网…

图像处理实验四(Adaptive Filter)

一、Adaptive Filter简介 自适应滤波器&#xff08;Adaptive Filter&#xff09;是一种能够根据输入信号的统计特性自动调整自身参数以达到最佳滤波效果的滤波器。它广泛应用于信号处理领域&#xff0c;如信道均衡、系统识别、声学回波抵消、生物医学、雷达、波束形成等模块。 …

计算机网络(8)数据链路层之子层

上一篇已经讲到数据链路层可以分为两个子层&#xff0c;这次将重点讲解子层的作用和ppp协议 数据链路层的子层 数据链路层通常被分为两个子层&#xff1a; 逻辑链路控制子层&#xff08;LLC&#xff0c;Logical Link Control&#xff09;&#xff1a; LLC子层负责在数据链路…