文章目录
- 1、概述
- 2、通信效果
- 2.1、Request-Reply(请求-响应模式)
- 2.2、Publish-Subscribe(订阅-发布模式)
- 3、方式选择
- 3.1、准备用 Visual Studio-C++ 方式
- 3.1.1、找到 Builds 文件夹
- 3.1.2、查看 deprecated-msvc 下的 libzmq.sln 文件
- 3.1.3、使用 Visual Studio 打开 libzmq.sln 解决方案
- 3.1.4、修改 libzmq.import.props 文件
- 3.1.5、编译生成
- 3.2、准备用 PyCharm-Python 方式
- 3.2.1、安装
- 3.2.1.1、命令安装
- 3.2.1.2、IDE安装
- 3.2.2、查看是否安装成功
- 4、Request-Reply(请求响应模式)
- 4.1、Request-Reply模式概述
- 4.2、Client 源代码
- 4.3、Server 源代码
- 5、Publish/Subscribe(订阅-发布模式)
- 5.1、Pub-Subs模式概述
- 5.2、发布者 源代码
- 5.3、订阅者 源代码
1、概述
在前一篇文章中提到了 ØMQ (ZeroMQ) ,是一个基于消息队列的多线程网络库,它封装了网络通信、消息队列、线程调度等功能,向上层提供简洁的API,应用程序通过加载库文件,调用API函数来实现高性能网络通信。
本篇文章重点讲述下,在客户端上如何使用 ZeroMQ 与 服务端的 ZeroMQ 进行通信,主要讲述两种常用模式:
1.Request-Reply(请求响应模式)
2.Publish-Subscribe(订阅-发布模式)
相关文章:
- 【Qt 学习之路】在 Qt 使用 ZeroMQ:https://shazhenyu.blog.csdn.net/article/details/136051050?spm=1001.2014.3001.5502
2、通信效果
2.1、Request-Reply(请求-响应模式)
Client:
Server:
2.2、Publish-Subscribe(订阅-发布模式)
发布者:
订阅者:
3、方式选择
3.1、准备用 Visual Studio-C++ 方式
3.1.1、找到 Builds 文件夹
在 ZeroMQ 的 builds 文件夹下,有很多种编译方法,为了方便演示,本文选择msvc进行演示。
3.1.2、查看 deprecated-msvc 下的 libzmq.sln 文件
3.1.3、使用 Visual Studio 打开 libzmq.sln 解决方案
3.1.4、修改 libzmq.import.props 文件
用 Notepad++ 打开 libzmq.import.props 文件,修改 5处 路径,全部改成…\bin【注意:去掉“…\libzmq\”】
- 原:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<_PropertySheetDisplayName>ZMQ Import Settings</_PropertySheetDisplayName>
</PropertyGroup>
<!-- User Interface -->
<ItemGroup Label="BuildOptionsExtension">
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)libzmq.import.xml" />
</ItemGroup>
<!-- Configuration -->
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(Option-sodium)' == 'true'">ZMQ_USE_LIBSODIUM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Option-openpgm)' == 'true'">ZMQ_HAVE_OPENPGM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Option-gssapi)' == 'true'">HAVE_LIBGSSAPI_KRB5;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Option-draftapi)' == 'true'">ZMQ_BUILD_DRAFT_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<!-- Linkage -->
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\..\libzmq\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Linkage-libzmq)' == 'static' Or '$(Linkage-libzmq)' == 'ltcg'">ZMQ_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies Condition="'$(Linkage-libzmq)' != ''">libzmq.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories Condition="$(Configuration.IndexOf('Debug')) != -1">$(ProjectDir)..\..\..\..\..\libzmq\bin\$(PlatformName)\Debug\$(PlatformToolset)\$(Linkage-libzmq)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="$(Configuration.IndexOf('Release')) != -1">$(ProjectDir)..\..\..\..\..\libzmq\bin\$(PlatformName)\Release\$(PlatformToolset)\$(Linkage-libzmq)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<!-- Copy -->
<Target Name="Linkage-libzmq-dynamic" AfterTargets="AfterBuild" Condition="'$(Linkage-libzmq)' == 'dynamic'">
<Copy Condition="$(Configuration.IndexOf('Debug')) != -1"
SourceFiles="$(ProjectDir)..\..\..\..\..\libzmq\bin\$(PlatformName)\Debug\$(PlatformToolset)\dynamic\libzmq.dll"
DestinationFiles="$(TargetDir)libzmq.dll"
SkipUnchangedFiles="true" />
<Copy Condition="$(Configuration.IndexOf('Debug')) != -1"
SourceFiles="$(ProjectDir)..\..\..\..\..\libzmq\bin\$(PlatformName)\Debug\$(PlatformToolset)\dynamic\libzmq.pdb"
DestinationFiles="$(TargetDir)libzmq.pdb"
SkipUnchangedFiles="true" />
<Copy Condition="$(Configuration.IndexOf('Release')) != -1"
SourceFiles="$(ProjectDir)..\..\..\..\..\libzmq\bin\$(PlatformName)\Release\$(PlatformToolset)\dynamic\libzmq.dll"
DestinationFiles="$(TargetDir)libzmq.dll"
SkipUnchangedFiles="true" />
</Target>
<!-- Messages -->
<Target Name="libzmq-info" BeforeTargets="AfterBuild" Condition="'$(Linkage-libzmq)' == 'dynamic'">
<Message Text="Copying libzmq.dll -> $(TargetDir)libzmq.dll" Importance="high"/>
<Message Text="Copying libzmq.pdb -> $(TargetDir)libzmq.pdb" Importance="high" Condition="$(Configuration.IndexOf('Debug')) != -1" />
</Target>
</Project>
- 改:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<_PropertySheetDisplayName>ZMQ Import Settings</_PropertySheetDisplayName>
</PropertyGroup>
<!-- User Interface -->
<ItemGroup Label="BuildOptionsExtension">
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)libzmq.import.xml" />
</ItemGroup>
<!-- Configuration -->
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(Option-sodium)' == 'true'">ZMQ_USE_LIBSODIUM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Option-openpgm)' == 'true'">ZMQ_HAVE_OPENPGM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Option-gssapi)' == 'true'">HAVE_LIBGSSAPI_KRB5;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Option-draftapi)' == 'true'">ZMQ_BUILD_DRAFT_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<!-- Linkage -->
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\..\libzmq\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Linkage-libzmq)' == 'static' Or '$(Linkage-libzmq)' == 'ltcg'">ZMQ_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies Condition="'$(Linkage-libzmq)' != ''">libzmq.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories Condition="$(Configuration.IndexOf('Debug')) != -1">$(ProjectDir)..\..\..\..\bin\$(PlatformName)\Debug\$(PlatformToolset)\$(Linkage-libzmq)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="$(Configuration.IndexOf('Release')) != -1">$(ProjectDir)..\..\..\..\bin\$(PlatformName)\Release\$(PlatformToolset)\$(Linkage-libzmq)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<!-- Copy -->
<Target Name="Linkage-libzmq-dynamic" AfterTargets="AfterBuild" Condition="'$(Linkage-libzmq)' == 'dynamic'">
<Copy Condition="$(Configuration.IndexOf('Debug')) != -1"
SourceFiles="$(ProjectDir)..\..\..\..\bin\$(PlatformName)\Debug\$(PlatformToolset)\dynamic\libzmq.dll"
DestinationFiles="$(TargetDir)libzmq.dll"
SkipUnchangedFiles="true" />
<Copy Condition="$(Configuration.IndexOf('Debug')) != -1"
SourceFiles="$(ProjectDir)..\..\..\..\bin\$(PlatformName)\Debug\$(PlatformToolset)\dynamic\libzmq.pdb"
DestinationFiles="$(TargetDir)libzmq.pdb"
SkipUnchangedFiles="true" />
<Copy Condition="$(Configuration.IndexOf('Release')) != -1"
SourceFiles="$(ProjectDir)..\..\..\..\bin\$(PlatformName)\Release\$(PlatformToolset)\dynamic\libzmq.dll"
DestinationFiles="$(TargetDir)libzmq.dll"
SkipUnchangedFiles="true" />
</Target>
<!-- Messages -->
<Target Name="libzmq-info" BeforeTargets="AfterBuild" Condition="'$(Linkage-libzmq)' == 'dynamic'">
<Message Text="Copying libzmq.dll -> $(TargetDir)libzmq.dll" Importance="high"/>
<Message Text="Copying libzmq.pdb -> $(TargetDir)libzmq.pdb" Importance="high" Condition="$(Configuration.IndexOf('Debug')) != -1" />
</Target>
</Project>
3.1.5、编译生成
按照官网的方式,这一步应该是成功了,但是目前是失败,还少文件,可能是包有问题
3.2、准备用 PyCharm-Python 方式
3.2.1、安装
3.2.1.1、命令安装
pip install pyzmq
3.2.1.2、IDE安装
安装图种方法安装 pyzmq25.1.2 版本
3.2.2、查看是否安装成功
直接打印 zmq 版本号 试试
import zmq
print(zmq.__version__)
由于 Python 相对快捷,本文后续以 Python 进行讲述
4、Request-Reply(请求响应模式)
4.1、Request-Reply模式概述
消息双向的,有来有往。
Client请求的消息,Server必须答复给Client。
Client在请求后,Server必须回响应,注意:Server不返回响应会报错。
Server和Client都可以是1:N的模型。通常把1认为是Server,N认为是Client。
更底层的端点地址是对上层隐藏的,每个请求都隐含回应地址,而应用则不关心它。
ZMQ 可以很好的支持路由功能(实现路由功能的组件叫做 Device),把 1:N 扩展为 N:M(只需要加入若干路由节点)。
4.2、Client 源代码
# client.py
import zmq
context = zmq.Context()
# Socket to talk to server
print("Connecting to 5555 server…")
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
socket.send(b"Hello, I'm Mr.Sha Client.")
# Get the reply.
message = socket.recv()
print(f"Received reply [ {message} ]")
4.3、Server 源代码
# server.py
import time
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# Wait for next request from client
message = socket.recv()
print("Received request: %s" % message)
# Do some 'work'
time.sleep(1)
# Send reply back to client
socket.send(b"Hello, I'm server.")
5、Publish/Subscribe(订阅-发布模式)
5.1、Pub-Subs模式概述
消息单向,有去无回
一个发布端,多个订阅端;发布端只管产生数据,发布端发布一条消息,可被多个订阅端同时收到。
发布者不必关心订阅者的加入和离开,消息会以 1:N 的方式扩散到每个订阅者。
广播所有client,没有队列缓存,断开连接数据将永远丢失。
如果Publish端开始发布信息时,Subscribe端尚未连接进来,则这些信息会被直接丢弃。
PUB和SUB谁bind谁connect并无严格要求(虽本质并无区别),但仍建议PUB使用bind,SUB使用connect
使用SUB设置一个订阅时,必须使用zmq_setsockopt()对消息进行过滤
这里直接引用官方文档的例子,订阅者再稍作改装!
5.2、发布者 源代码
类似于一个天气更新服务器,向订阅者发送天气更新,内容包括邮政编码、温度、湿度等信息
# Publisher.py
import zmq
from random import randrange
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5556")
while True:
zipcode = randrange(1, 100000)
temperature = randrange(-80, 135)
relhumidity = randrange(10, 60)
socket.send_string("%i %i %i" % (zipcode, temperature, relhumidity))
5.3、订阅者 源代码
它监听发布者更新的数据流,过滤只接收与特定邮政编码相关的天气信息,默认接收接收10条数据
# Subscribe.py
import sys
import zmq
# Socket to talk to server
context = zmq.Context()
socket = context.socket(zmq.SUB)
print("Collecting updates from weather server...")
socket.connect("tcp://localhost:5556")
# Subscribe to zipcode, default is NYC, 10001
zip_filter = sys.argv[1] if len(sys.argv) > 1 else "10001"
# Python 2 - ascii bytes to unicode str
if isinstance(zip_filter, bytes):
zip_filter = zip_filter.decode('ascii')
socket.setsockopt_string(zmq.SUBSCRIBE, zip_filter)
# Process 5 updates
total_temp = 0
for update_nbr in range(5):
string = socket.recv_string()
zipcode, temperature, relhumidity = string.split()
total_temp += int(temperature)
print("zipcode, temperature, relhumidity:" + string)
print(
"Average temperature for zipcode '%s' was %dF"
% (zip_filter, total_temp / (update_nbr + 1))
)