nanomsg使用

nanomsg是一个消息通信组件 - zeromq的作者之一用C语言重写的通信框架,
使用宽松的MIT许可开源,小、轻、快,非常方便,介绍我就不多写了,下面我具体的讲用法,用了你就知道是怎么回事。

pull/push 单向管道推送模式

push/pull套接字结合使用可实现消息队列的扇出模式,
这是一个1对多的模式,服务端不能有多个,但客户端可以多个。
官网上给的这个图是1对1的,不要误解(他实际上是用于1对多的):

nanomsg使用_第1张图片

服务端或客户端不管谁先启动,都会等待对方连接,
注意pub/sub模式的服务端启动后是不会等待客户端来连接的,而push/pull就完全相反必须双方连接好了再推送,可以想象为一个管道,两头都接上才能传输数据。

pub/sub模式中,服务端一旦跟客户端连接,中间就绝不会丢弃消息,
就类似于一个单向的数据流,服务端只发不收,客户端只收不发,一个消息不会重复发给多个客户端
一般用于任务分派、负载均衡。

push服务端示例:
import console; 

import nnmsg;
var sock = nnmsg.socket.push();
sock.bind (
"ws://*:7715"

console.log(
"push服务端等待连接中,没有人连接是不会发送的")

for(i=1;100;1){
      
    sock.send(
"分发任务ID:" + i );
    console.log(
"分发任务ID:" + i )
    
sleep(1000)


console.pause(
true);

pull客户端示例:

import console; 

import nnmsg;
var sock = nnmsg.socket.pull();

console.log("客户端等待连接")
if( sock.connect("ws://127.0.0.1:7715") ){
    
    
var s = sock.recv();
    
while(s){
        console.log(
"服务器返回:",s );
        s = sock.recv();
    }
    
}
console.pause(
true);
   

千万不要以为这有什么呀,自己写个套接字也可以推送点东西,

nanomsg可没有这么简单,表面上看起来他好像是一个传输流,实际上他并不是。

 

nanomsg不像传统套接字那样,一定要服务端先启动,客户端后面跑过来连接,

他也可以客户端先启动,服务端后启动。

 

而且在连接传输的过程中,服务端,或者是客户端都可以中途断线,

下次再启动,可以继续工作,很神奇是吧?!

sub/pub 消息广播模式

sub/pub套接字结合使用可实现消息广播模式(Topics && Broadcast)
服务端只管发布,不管客户端是否连接,也不管是不是丢消息,但客户端连接上来以后就不会丢消息。


nanomsg使用_第2张图片
这个模式与push/pull的区别是很明显的,
不会像push/pull那样一定要等待双方连接才传输数据。
pub服务端只管自己顾自己的发布消息,而不管别人有没有收到。
一个典型的例子就是报时服务器,下面看代码。

pub服务端:

import console; 

import nnmsg;
var sock = nnmsg.socket.pub();
sock.bind (
"ws://*:7713"

console.log(
"pub服务端已启动,不管客户端是不是连接上来,也不考虑有没有人接收消息")

for(i=1;100;1){
      
    sock.send(
"报时:" + tostring( time() ) );
    console.log(
"报时:" + tostring( time() )  )
    
sleep(1000)


console.pause(
true);

sub客户端:

import console; 

import nnmsg;
var sock = nnmsg.socket.sub();

console.log(
"客户端等待连接")

//一定要指定订阅的主题前缀,指定为空订阅所有主题
sock.subscribe("报时:");

if( sock.connect("ws://127.0.0.1:7713") ){
    
    
var s = sock.recv();
    
while(s){
        console.log(
"服务器返回:",s );
        s = sock.recv();
    }
    
}
console.pause(
true);
   

这个sub客户端有一点特别,一定要用 sock.subscribe("报时:"); 指定你感兴趣的消息,

这个函数的参数指定要订阅消息的前几个字符,这几个字符没有特别格式的要求,只要消息是由这几个字符开始就可以。

 

pus/sub模式里,服务端的同一个消息可能被多个客户端接收,也可能根本没有客户端接收,

push/pull则完全不同 - 服务端消息一定会被一个客户端,并且也只会被一个客户端取走。

surveyor/respondent调查者模式

surveyor/respondent套接字结合使用可实现调查模式,
surveyort负责发出调查问题,而respondent客户端则负责回应。


nanomsg使用_第3张图片
surveyor 服务端示例:

import console; 

import nnmsg;
var sock = nnmsg.socket.surveyor();
sock.bind (
"ws://*:7717"

console.log(
"已启动 Survey(WebSocket协议) 服务端")
while (1){   
    
var msg  = sock.send("客户端你好,现在几点了?");
    
while(1){
        
var tm,err = sock.recvTable();
        
if(!tm){
            
if(nnmsg.lasterrno() == 0x9523DFE/*_NN_EFSM*/ ) break ;
            
else continue ;  
        }
        console.log( tm )
    }
     
    console.log(
"调查完了")   
}
  

respondent客户端:

import console; 
import nnmsg;

console.log(
"Survey(WebSocket协议)客户端已启动")
var sock = nnmsg.socket.respondent();

if( sock.connect("ws://127.0.0.1:7717") ){
    
while(1){
       
var msg  = sock.recv();
        console.dump( 
"收到:",msg );
        sock.sendTable( time() )
    } 
}

console.pause(
true);

 

pair 端对端双向通信模式

pair套接字,服务端和客户端可以1对1的收发消息,
nanomsg使用_第4张图片

pair 服务端:
import console; 

import nnmsg;
var sock = nnmsg.socket.pair();
sock.bind (
"ws://*:7710"

console.log(
"已启动 WebSocket 服务端")
while (1){   
    
var msg  = sock.recv();
    console.dump( 
"客户端:",msg );    
    sock.send(
"客户端你还好吗?!"
}

console.pause(
true);

pair 客户端:

import console; 

import nnmsg;
var sock = nnmsg.socket.pair();
if( sock.connect("ws://127.0.0.1:7710") ){
    sock.send(
"hello"
    console.log(
"服务端:",sock.recv() )
}
console.pause(
true);
   

bus 消息总线模式

一个消息总线上可以有多个套接字,
每个套接字即是服务端可以启动监听,也是客户端可以同时连接多个其他的套接字。

连接到消息总线的任何一个套接字发送消息,消息总线上的其他套接字都能收到,一个套接字发出的消息,
其他套接字有可能重复的接收到多次(这个就好比街头听到的小道消息,可能由不同的人告诉你)。
但是套接字永远不会收到自己发的消息。


nanomsg使用_第5张图片

下面请看演示,消息总线里不分服务端或者客户端:
import win.ui;
/*DSG{{*/
var winform = win.form(text="nanomsg消息总线模式 演示";right=759;bottom=469)
winform.add(
edit={cls=
"edit";left=17;top=18;right=739;bottom=445;db=1;dl=1;dr=1;dt=1;edge=1;multiline=1;vscroll=1;z=1}
)

/*}}*/

winform.show() 

//用于启动一个节点线程
var joinbus = function(port,winform){
    
import win;
    
import nnmsg;
    
    
var sock = nnmsg.socket.bus();
    sock.bind (
"tcp://*:" + port) 
    
    
var nodes = thread.get("nnmsg-bus") : {}; 
    
for(i,addr in nodes){ 
        sock.connect(addr)
//连接到消息总线上所有其他的节点
    }
    
//把自己的地址写到消息总线上去
    table.push(nodes,"tcp://127.0.0.1:" + port);
    thread.set(
"nnmsg-bus",nodes ) 
      
    
sleep(100);//等待其他节点加入
    sock.send(port + "发送:我是新人,我今天才刚知道这个消息总线" ) 
    
    
while( win.isWindow(winform.hwnd) ){   
        
var msg  = sock.recv();
        winform.edit.print(port + 
"接收:" + msg)
    }
}


//创建一大堆节点模拟消息总线
for(port=7591;7599){
    thread.invoke(joinbus,port,winform)  
sleep(100);
}

win.loopMessage();

运行示例可以看到,这个模式发送的消息发送的还是有些激烈的,所以这种模式只适合在本地局域网内使用。

使用WebSocket客户端与nanomsg交互

nanomsg实现各种模式的代码惊人的简洁,但简洁不等于简单。
并且各种编程语言里基本都有nanomsg的实现,使用nanomsg有良好的可扩展性。

nanomsg不但可以实现多种不同的通信模式,也可以选择多种不同的通信协议。
例如上面的我写的消息总线模式 - 使用的是tcp协议,而其他的模式使用的是WebSocket协议。
尤其是其中的WebSocket协议非常有趣,我们可以自己写一个WebSocket去连接nanomsg的服务端,并与nanomsg交互。

最初尝试的时候,我失败了!
在aardio中使用 web.socket.client 对象去连接 nanomsg,nanomsg很不高兴的拒绝了连接。

于是,我想起了 wsock.tcp.simpleHttpServer 这个好东西,
首先我用 wsock.tcp.simpleHttpServer启动了一个HTTP服务端 - 用来冒充nanomsg服务端。
然后用 nanamsg创建了一个WebSocket客户端连接上去,在HTTP服务器上输出发过来的数据包,
发现了nanomsg的小秘密,多了一个Sec-WebSocket-Protocol的HTTP头。

好吧,下面我们看演示代码。

nanomsg 服务端:
import console; 

import nnmsg;
var sock = nnmsg.socket.pub();
sock.bind (
"ws://*:7725"

console.log(
"pub服务端已启动,可以打开aardio websocket客户端试一下了")

for(i=1;100;1){
      
    sock.send(
"报时:" + tostring( time() ) );
    console.log(
"报时:" + tostring( time() )  )
    
sleep(1000)


console.pause(
true);
 

下面是aardio实现的WebSocket客户端:

import win.ui;
/*DSG{{*/
var winform = win.form(text="WebSocket客户端演示";right=770;bottom=467)
winform.add(
btnClose={cls=
"button";text="断开";left=681;top=295;right=757;bottom=333;db=1;dr=1;z=6};
btnOpen={cls=
"button";text="连接WebSocket服务端";left=501;top=295;right=655;bottom=333;db=1;dr=1;z=2};
btnSend={cls=
"button";text="发送数据";left=567;top=380;right=764;bottom=456;db=1;dr=1;z=4};
cbSecWebSocketProtocol={cls=
"combobox";left=301;top=298;right=481;bottom=324;edge=1;items={};mode="dropdown";z=7};
txtMessage={cls=
"edit";left=29;top=22;right=741;bottom=285;db=1;dl=1;dr=1;dt=1;edge=1;multiline=1;z=1};
txtSend={cls=
"edit";text="WebSocket测试";left=25;top=382;right=554;bottom=457;db=1;dl=1;dr=1;edge=1;multiline=1;z=3};
txtUrl={cls=
"edit";text="ws://127.0.0.1:7725";left=29;top=295;right=269;bottom=331;db=1;dl=1;dr=1;edge=1;z=5}
)

/*}}*/

winform.cbSecWebSocketProtocol.items ={
    
"pub.sp.nanomsg.org";
    
"sub.sp.nanomsg.org";
    
"pair.sp.nanomsg.org";
    
"req.sp.nanomsg.org";
    
"rep.sp.nanomsg.org";
    
"surveyor.sp.nanomsg.org";
    
"respondent.sp.nanomsg.org";
    
"push.sp.nanomsg.org";
    
"pull.sp.nanomsg.org";
    
"bus.sp.nanomsg.org";
}


import web.socket.client;
var ws = web.socket.client();
ws.heartbeatInterval = - 1;
//sub端不能发任何数据

ws.onOpen = function(){
    winform.cbSecWebSocketProtocol.disabled = 
true;
}

ws.onClose = 
function(){
    winform.txtMessage.print(
"onClose");
    winform.cbSecWebSocketProtocol.disabled = 
false;
    winform.btnSend.disabledText = 
null;
}

ws.onError = 
function(err){
    winform.txtMessage.print(
"onError",err);
}

ws.onMessage = 
function(msg){
    winform.txtMessage.print(msg.data); 
}

winform.btnSend.oncommand = 
function(id,event){
    ws.send(winform.txtSend.text)   
}

winform.btnOpen.oncommand = 
function(id,event){
    
//加上这句,假装自己是nanomsg客户端
    ws.headers ={
        [
"Sec-WebSocket-Protocol"] = winform.cbSecWebSocketProtocol.selText;
    }
    
    
if( winform.cbSecWebSocketProtocol.selText == "pub.sp.nanomsg.org" ){
        winform.btnSend.disabledText = 
"此模式不支持发送"
    }
    ws.connect(winform.txtUrl.text);
}

winform.btnClose.oncommand = 
function(id,event){
    ws.close();
}

winform.show() 
win.loopMessage();


运行示例,奇迹发生了:

nanomsg使用_第6张图片

你可能感兴趣的:(C/C++,网络,架构)