文章目录

  • 前言
  • 一、libcurl:高可移植、功能丰富的通信库
  • 二、cpr:更现代、更易用的通信库
  • 三、ZMQ:高效、快速、多功能的通信库
  • 总结

前言

这几天浏览了一下极客时间罗剑锋老师的《C++实战笔记》课程,其中提到了一些好用的网络库,简单做个记录,便于日后学习。
课程链接链接: 罗剑锋的C++实战笔记


一、libcurl:高可移植、功能丰富的通信库

libcurl 经过了多年的开发和实际项目的验证,非常稳定可靠,拥有上百万的用户,其中不乏 Apple、Facebook、Google、Netflix 等大公司。它最早只支持 HTTP 协议,但现在已经扩展到支持所有的应用层协议,比如 HTTPS、FTP、LDAP、SMTP 等,功能强大。libcurl 使用纯 C 语言开发,兼容性、可移植性非常好,基于 C 接口可以很容易写出各种语言的封装,所以 Python、PHP 等语言都有 libcurl 相关的库。因为 C++ 兼容 C,所以我们也可以在 C++ 程序里直接调用 libcurl 来收发数据。
libcurl 常用的还是 HTTP,libcurl 的接口可以粗略地分成两大类:easy 系列和 multi 系列。其中,easy 系列是同步调用,比较简单;multi 系列是异步的多线程调用,比较复杂。通常情况下,我们用 easy 系列就足够了。使用 libcurl 收发 HTTP 数据的基本步骤有 4 个:
1,使用 curl_easy_init() 创建一个句柄,类型是 CURL*。但我们完全没有必要关心句柄的类型,直接用 auto 推导就行;
2,使用 curl_easy_setopt() 设置请求的各种参数,比如请求方法、URL、header/body 数据、超时、回调函数等。这是最关键的操作;
3,使用 curl_easy_perform() 发送数据,返回的数据会由回调函数处理;
4,使用 curl_easy_cleanup() 清理句柄相关的资源,结束会话;


#include <curl/curl.h>auto curl = curl_easy_init();        // 1,创建CURL句柄
assert(curl);curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com/"); // 2,设置请求URIauto res = curl_easy_perform(curl);   // 3,发送数据
if (res != CURLE_OK) {                // 检查是否执行成功cout << curl_easy_strerror(res) << endl;
}curl_easy_cleanup(curl);             // 4,清理句柄相关的资源

这段代码的重点是调用 curl_easy_setopt() 设置了 URL,请求百度,其他的都使用默认值即可。由于没有设置自己的回调函数,所以 libcurl 会使用内部的默认回调,把得到的 HTTP 响应数据输出到标准流,也就是直接打印到屏幕上。
libcurl 是 C 语言实现的,所以回调函数必须是函数指针。不过,C++11 允许写 lambda 表达式,这利用了一个特别规定:无捕获的 lambda 表达式可以显式转换成一个函数指针。注意一定要是“无捕获”,也就是说 lambda 引出符“[]”必须是空的,不能捕获任何外部变量。所以,只要多做一个简单的转型动作,就可以用 lambda 表达式直接写 libcurl 的回调:


// 回调函数的原型
size_t write_callback(char* , size_t , size_t , void* );curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,        // 设置回调函数(decltype(&write_callback))      // decltype获取函数指针类型,显式转换[](char *ptr, size_t size, size_t nmemb, void *userdata)// lambda{cout << "size = " << size * nmemb << endl;   // 简单的处理return size * nmemb;                // 返回接收的字节数}
);

二、cpr:更现代、更易用的通信库

cpr 是对 libcurl 的一个 C++11 封装,使用了很多现代 C++ 的高级特性,对外的接口模仿了 Python 的 requests 库,非常简单易用。
和 libcurl 相比,cpr 用起来真的是太轻松了,不需要考虑什么初始化、设置参数、清理等杂事,一句话就能发送 HTTP 请求:


#include <cpr/cpr.h>                            auto res = cpr::Get(                           // GET请求    cpr::Url{"http://openresty.org"}   // 传递URL
);

你也不用写回调函数,HTTP 响应就是函数的返回值,用成员变量 url、header、status_code、text 就能够得到报文的各个组成部分:

cout << res.elapsed << endl;            // 请求耗费的时间cout << res.url << endl;               // 请求的URL
cout << res.status_code << endl;       // 响应的状态码
cout << res.text.length() << endl;     // 响应的body数据for(auto& x : res.header) {            // 响应的头字段cout << x.first << "=>"            // 类似map的结构<< x.second << endl;
}

在 cpr 里,HTTP 协议的概念都被实现为相应的函数或者类,内部再转化为 libcurl 操作,主要的有:
(1) GET/HEAD/POST 等请求方法,使用同名的 Get/Head/Post 函数;
(2) URL 使用 Url 类,它其实是 string 的别名;
(3) URL 参数使用 Parameters 类,KV 结构,近似 map;
(4) 请求头字段使用 Header 类,它其实是 map 的别名,使用定制的函数实现了大小写无关比较;
(5) Cookie 使用 Cookies 类,也是 KV 结构,近似 map;
(6) 请求体使用 Body 类;
(7) 超时设置使用 Timeout 类;
这些函数和类的用法都非常自然、符合思维习惯,而且因为可以使用 C++11 的花括号“{}”初始化语法:


const auto url = "http://openresty.org"s;  // 访问的URLauto res1 = cpr::Head(                    // 发送HEAD请求cpr::Url{url}                 // 传递URL
);auto res2 = cpr::Get(                     // 发送GET请求cpr::Url{url},               // 传递URLcpr::Parameters{             // 传递URL参数{"a", "1"}, {"b", "2"}}
);auto res3 = cpr::Post(                  // 发送POST请求cpr::Url{url},             // 传递URLcpr::Header{                // 定制请求头字段{"x", "xxx"},{"expect",""}},cpr::Body{"post data"},    // 传递body数据cpr::Timeout{200ms}       // 超时时间
);

cpr 也支持异步处理,但它内部没有使用 libcurl 的 multi 接口,而是使用了标准库里的 future 和 async,和 libcurl 的实现相比,既简单又好理解。异步接口与同步接口的调用方式基本一样,只是名字多了个“Async”的后缀,返回的是一个 future 对象。你可以调用 wait() 或者 get() 来获取响应结果:


auto f = cpr::GetAsync(                    // 异步发送GET请求cpr::Url{"http://openresty.org"}
);auto res = f.get();                       // 等待响应结果
cout << res.elapsed << endl;              // 请求耗费的时间

三、ZMQ:高效、快速、多功能的通信库

Zero Message Queue——零延迟的消息队列,意味着它除了可以收发数据外,还可以用作消息中间件,解耦多个应用服务之间的强依赖关系,搭建高效、有弹性的分布式系统。
ZMQ支持多种模式,而且模式之间也可以组合应用,同时也支持多种通信协议。ZMQ的具体应用可以参见这位大佬:https://blog.csdn.net/zzhongcy/category_1922857.html
使用中有些需要注意的细节:
一个是 ZMQ 环境的线程数。它的默认值是 1,太小了,适当增大一些就可以提高 ZMQ 的并发处理能力。用的是 4~6,具体设置为多少最好还是通过性能测试来验证下。
另一个是收发消息时的本地缓存数量,ZMQ 的术语叫 High Water Mark。如果收发的数据过多,数量超过 HWM,ZMQ 要么阻塞,要么丢弃消息。HWM 需要调用套接字的成员函数 setsockopt() 来设置,注意收发使用的是两个不同的标志:


sock.setsockopt(ZMQ_RCVHWM, 1000);     // 接收消息最多缓存1000条
sock.setsockopt(ZMQ_SNDHWM, 100);      // 发送消息最多缓存100条

总结

1,libcurl 是一个功能完善、稳定可靠的应用层通信库,最常用的就是 HTTP 协议;
2,cpr 是对 libcurl 的 C++ 封装,接口简单易用;
3,libcurl 和 cpr 都只能作为客户端来使用,不能编写服务器端应用;
4,ZMQ 是一个高级的网络通信库,支持多种通信模式,可以把消息队列功能直接嵌入应用程序,搭建出高效、灵活、免管理的分布式系统(ZMQ的作者又开发了一个新的网络通信库nanomq,改进了一些ZMQ的设计不足,但应用不是很广);
C++23预计会加入networking,该库基于已有多年实践的 boost.asio,采用前摄器模式(Proactor)统一封装了操作系统的各种异步机制(epoll、kqueue、IOCP),而且支持协程。有了它,我们的网络通信工作就会更加轻松。
在项目开发过程中还用过ACE、Hp-Socket,也都各有优劣。在游戏开发中,还有应用poco的,虽然下载过源码,简单跑过,但是没什么深入研究。

C++常用的一些网络库相关推荐

  1. C++ 第三方常用网络库

    From:https://www.cnblogs.com/aitantianderuangutou/p/11416902.html (1) ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库 ...

  2. C++常用库之网络库

    C++一个很大的用途就是作为网络层组件的开发语言.C++开发的第三方网络库也比较多.其实,c语言下的网络库也不少.现在简单介绍一下. ACE库 ACE是一个大型的中间件产品,代码有几十万行,非常宏大, ...

  3. IOS常用代码总结 - 第三方库部分

    1 SBJson的使用 JSON是一种数据交换语言,和XML是同样用途的.不过JSON的体积要比XML小,也就意味着在网络传输中 速度会比XML更快. 这里可以看到更多关于json的资料:http:/ ...

  4. muduo网络库源码阅读Step by Step

    Posted on: Nov 26 2015 Categories: muduo C++ Tags: muduo 一般写服务端程序都需要有一个称手的网络库来帮我们处理琐碎的网络通信细节,比如连接的建立 ...

  5. 《Linux多线程服务端编程:使用muduo C++网络库》书摘6.6.2节

    6.6.2 常见的并发网络服务程序设计方案 W. Richard Stevens 的<UNIX 网络编程(第2 版)>第27 章"Client-ServerDesign Alte ...

  6. 发布一个基于 Reactor 模式的 C++ 网络库

    发布一个基于 Reactor 模式的 C++ 网络库 陈硕 (giantchen_AT_gmail) Blog.csdn.net/Solstice 2010 Aug 30 本文主要介绍 muduo 网 ...

  7. Linux 的常用系统及网络命令

    (转载自http://blog.chinaunix.net/uid-8031155-id-2518955.html) Linux下常用命令收集整理 Linux 的常用网络命令 计算机网络的主要优点是能 ...

  8. muduo C++网络库的学习笔记

    文章目录 10.3.C++链接linking 10.3.2 inline函数 10.3.3 模板 10.3.4 虚函数 10.4 工程项目中头文件的使用规则 10.4.2 头文件的使用规则 11.1 ...

  9. 《Linux多线程服务端编程:使用muduoC++网络库》学习笔记

    文章目录 第1章 线程安全的对象生命期管理 1.1 当析构函数遇到多线程 1.1.1 线程安全的定义 1.1.3 线程安全实例 1.2 对象的创建很简单 1.3 销毁很难 1.4 线程安全的Obser ...

最新文章

  1. Spring Cloud Feign的两种使用姿势
  2. java进制转化_【Java学习笔记之四】java进制转化
  3. network location awareness启动不了_【新手看过来】无钥匙启动功能
  4. java基础学完_学完Java基础应该继续学什么?
  5. matlab解微分方程组_MATLAB编程入门 求解常微分方程 通解 特解 数值解
  6. 浙江省单考单招计算机提前招,2018浙江省各校高职提前招生简章汇总及深度解析...
  7. how-to-set-java_home-environment-variable-on-mac-os-x
  8. SpringBoot整合CXF框架实现Webservice服务端
  9. 通过YYtext实现文本点击(类似微博效果)
  10. android 减小apk大小,Android Studio如何减小APK体积
  11. TWS耳机什么值得买?入耳式蓝牙耳机排行榜10强!
  12. 每个数据科学家都应该知道的10种机器学习方法
  13. MySQL详细安装步骤
  14. LRUCache的C++实现
  15. ThinkPHP 5.0 rewrite规则
  16. python 函数的使用方法
  17. gil 简述_求职面试常见问题:Python常见面试题全解析附答案
  18. 求树的直径的两种方法
  19. 什么是UNIX时间戳? 时间戳有什么用?
  20. mybatis倒叙排版

热门文章

  1. 2018 年上半年网络工程师考试上午真题
  2. python弹出输入框_Python实现使用tkinter弹出输入框输入数字, 具有确定输入和清除功能...
  3. 华三交换机irf 堆叠配置_H3C S5500-EI IRF堆叠的典型配置
  4. 用flash制作简单拼图游戏
  5. LTE-PHY物理资源划分(一)
  6. Theory and Applications of OFDM and CDMA: Wideband Wireless Communications
  7. 指纹识别所运用的计算机技术,门禁系统的指纹识别功能所运用的计算机技术有哪些...
  8. 《猫鼠游戏》想到的CTF场景。。。。。
  9. 被动套接字 主动套接字_了解网络套接字及其可能性
  10. Office Open XML 文档格式