通过C#使用ZeroMQ
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在发送消息前留点时间等待套接字连接完成以免消息丢失
请求/回答模式(REQ/REP)
该模式具有以下特征:
服务器使用 REP 类型套接字而客户端使用 REQ 类型套接字
客户端发送请求和接收答复,而服务器则接收请求并发送答复。
客户端可以连接到一个或多个服务器。在这种情况下,请求会在所有的服务器(Reps)
" + replyMsg + Environment.NewLine); socket.Send(replyMsg, Encoding.UTF8);
} } } 而客户端的代码如下: using (var context = ZmqContext.Create()) { using (var socket = context.CreateSocket(SocketType.REQ)) {
图 1-2 One Client-One Server
服务器的代码如下: using (var context = ZmqContext.Create()) {
using (var socket = context.CreateSocket(SocketType.REP)) {
foreach (var bindEndPoint in options.bindEndPoints) socket.Bind(bindEndPoint);
{
socket.Connect("tcp://127.0.0.1:5000");
socket.Send("My Reply", Encoding.UTF8);
var replyMsg = socket.Receive(Encoding.UTF8);
}
}
在代码的第一行,我们创建了一个上下文,然后从该上下文创建了有名的 ZeroMQ 套接字。与此
异步通讯
ZeroMQ 提供了异步通讯方式,这意味着即使在设置或关闭套接字连接、重新连接或进行消息 传递的时候,应用程序都不会被阻塞。与程序正常任务处理并行的,这些操作由 ZeroMQ 自身在 后台线程中进行管理。在需要时,ZeroMQ 会自动将消息 (无论是发件端或接受端) 放入队列中, 这一过程相当智能,消息会被推送到离接收端尽可能近的队列中。
头两行命令会分别运行 Rep 的一个实例。实例会在不同的端口号上等待连接(5000 和 5001)。 最后一个命令则运行连接到这两个实例的 Req 客户端。 执行上面的命令之后,我们得到下面的结果:
while (true)
{ Thread.Sleep(options.delay); var rcvdMsg = socket.Receive(Encoding.UTF8); Console.WriteLine("Received: " + rcvdMsg); var replyMsg = options.replyMessage.Replace("#msg#", rcvdMsg); Console.WriteLine("Sending :
传输协议
ZeroMQ 支持四类传输协议。每种传输协议由地址字符串来定义,该字符串由两部分组成: transport://endpoint。传输(transport) 部分指定了所使用的底层传输协议,端点(endpoint) 部分的格式则随着使用的协议而有所不同,具体如下:
TCP (tcp://hostname:port): 在主机之间进行通讯 INROC (inproc://name): 在同一进程的线程之间进行通讯(线程间) IPC (ipc:///tmp/filename): 同一主机的进程之间进行通讯 PGM (pgm://interface;address:port 和 epgm://interface;address:port): 多播通
讯
消息格式
ZeroMQ 默认可以发送或接收字符串和二进制的消息类型,但它对套接字之间传送的消息格式 不加限制。我们可以自由地选择消息编码,如 XML,JSON、 MessagePack…在本文中为了简单起 见,我们只使用字符串。
ZeroMQ 示例
这部分有关 ZeroMQ 的各种解决方案。在这里,我创建了几个小控制台程序,可以很容易地 测试不同情况下的通信模式。你需要用到两个库文件: libzmq.dll 和编译好的 clrzmq.dll。这 些小应用都有一组命令行参数(感谢 Giacomo Stelluti Scala 的开源项目:命令行参数分析器), 在应用程序后面带上开关参数 /? 就可以直接显示这些参数。此外我还为每种通讯模式的执行编 写了批处理文件,直接执行批处理就可以快速地执行同一模式。
项目生成时,将 libzmq.dll 会被拷贝到输出目录。
4. 在代码中添加 using 指令导入 clrzmq.dll 命名空间
现在可以开始编写简单的代码来使用 ZeroMQ 发送和接受消息了。请看下面的代码:
using (var context = ZmqContext.Create())
{
using (var socket = context.CreateSocket(SocketType.REQ))
break;
if (msgIndex == options.alterMessages.Count()) msgIndex = 0;
var reqMsg = options.alterMessages[msgIndex++] .Replace("#nb#", msgCptr.ToString("d2"));
之间循环, 一个请求被发送到某个服务器,下一个请求则被发送到下个服务器,如此进行下
去。
基于状态的模式: 客户端在发送另一个请求之前,必须先接收前一个请求的答复。而
服务器在接收另一个请求之前,必须答复前一个请求。
下面来看看两个使用该模式的客户端-服务器案例:
一对一关系 (一客户端对一服务器)
在这种情形下,一个客户端(Req)只连接到一个服务器(Rep),如下图所示:
通过 C#使用 ZeroMQ
1、ZeroMQ 通讯模式
ZeroMQ (也拼写作 ØMQ、 0MQ 或 ZMQ) 是个非常轻量级的开源消息队列软件。它没有独 立的服务器,消息直接从一个应用程序被发送到另一个应用程序。ZeroMQ 的学习和应用也非常简 单,它只有一个 C++ 编写成的单个库文件 libzmq.dll, 可以链接到任何应用程序中。如果要 在 .NET 环境中使用,我们需要用到一个 C# 编写的名为 clrzmq.dll 包装库。
同时,还定义了套接字类型。有了套接字,你就可以选择:
绑定到某一端点,然后等待其他套接字的连接
连接到某一端点
具体是绑定还是连接则取决于所使用的通讯模式,我们稍后再加解释。接下来是发送或接收消息
的部分。正如我们看到的,只用几行代码就建立了通讯。这样的代码在各种通讯模式中随处可见。
通讯模式
通信模式指定了一组连接的套接字之间的消息流。我们用一些形状和符号说明套接字之间的连接。 下面的关系图显示了套接字之间的基本连接:
foreach (var connectEndpoint in options.connectEndPoints) socket.Connect(connectEndpoint);
long msgCptr = 0; int msgIndex = 0;
while (true)
{ if (msgCptr == long.MaxValue) msgCptr = 0; msgCptr++; if (options.maxMessage >= 0) if (msgCptr > options.maxMessage)
图 1-1 套接字之间的基本连接
矩形表示的是某个包含一个或多个套接字应用程序。每个套接字可以绑定或连接到某个端点。绑
定到端点的套接字会等待来自其他套接字来连接。
在后续的许多代码中,您会看到在发送或接受消息之前有一个延迟(以毫秒记的某个数值)。添
加这个延迟的目的不外乎是:
延迟发送消息
模拟“忙”的状态
ZeroMQ 可以在 Windows、 OS X 和 Linux 等多种操作系统上运行, C、 C++、 C#、 Java、 Python 等语言都可以编写 ZeroMQ 应用程序…这使得不同平台上的不同应用程序之间可以相互 通讯。
ZeroMQ 的核心
ZeroMQ 的主要部分是套接字 Socket,不过它并未直接使用传统的套接字,而是在传统的套 接字 API 上提供了一个抽象层,这让用户从复杂和重复的编程任务中解脱出来。ZeroMQ 支持多 种类型的套接字 (类型被定义为套接字自身的一个属性)。发送端和接收端的套接字类型组合造 就了多种不同的通信模式,本文的后面我们会涉及到这一部分。
基础代码
在 Visual C# 项目中使用 ZeroMQ 用
2. 将 libzmq.dll 文件添加到项目中 (Add existing Item)(因为 clrzmq.dll 依赖于它)
3. 将 libzmq.dll 文件属性里的 Copy to Output Directory 设为 Copy if newer,这样在
图 1-3 一对一消息传递的运行结果
一对多(一客户端连接两个服务器)
现在我们用一个客户端 (Req) 连接到两个服务器 (Rep),如下图所示:
图 1-4 一对多的连接关系 现在双击 bin 目录下的 ReqRep_Patttern_2.bat 文件,该批处理包含下列命令: start "Server 1 (Rep)" cmd /T:8E /k Rep.exe -b tcp://127.0.0.1:5000 -r "#msg# Reply 1" -d 0 start "Server 2 (Rep)" cmd /T:8E /k Rep.exe -b tcp://127.0.0.1:5001 -r "#msg# Reply 2" -d 0 start "Client (Req)" cmd /T:8F /k Req.exe -c tcp://127.0.0.1:5000;tcp://127.0.0.1:5001 -m "Request #nb#" -x 5 -d 1000
Thread.Sleep(options.delay); Console.WriteLine("Sending : " + reqMsg); socket.Send(reqMsg, Encoding.UTF8); var replyMsg = socket.Receive(Encoding.UTF8); Console.WriteLine("Received: " + replyMsg + Environment.NewLine); } } } 双击 bin 目录下的 ReqRep_Patttern_1.bat 文件。该批处理包含下面命令: start "Server (Rep)" cmd /T:8E /k Rep.exe -b tcp://127.0.0.1:5000 -r "#msg# – Reply" -d 0 start "Client (Req)" cmd /T:8F /k Req.exe -c tcp://127.0.0.1:5000 -m "Request #nb#" -x 5 -d1000 第一个命令将启动一个有颜色(通过 /T:fg 参数)的 DOS 命令窗口,并运行 Rep.exe。Rep 将绑 定到端点 tcp://127.0.0.1:5000 并等待进入的请求。当请求到达时,它以收到的消息带上单词 “Reply”作为应答(通过 #msg# 宏)。发送应答前的延迟是 0 毫秒(可以通过 -d 的开关参数 指定)。 第二个命令会启动另一种颜色的 DOS 窗口,并执行应用 Req.exe。Req 将连接到端点 tcp://127.0.0.1:5000,然后它会发送 5 个消息,消息的内容是单词“Request”加上消息的序号 (通过 #nb# 宏)。消息之间的间隔是 1000 毫秒(-d 开关参数)。 运行批处理之后的结果如下: