https://blog.csdn.net/sy_123a/article/details/107692721
1 TIPC简介
TIPC是爱立信公司提出的透明进程间通信协议,主要用于高可用(HA)和集群系统中。它允许设计人员能够创建可以和其它应用快速可靠地通信应用,无须考虑在其它需要通信的应用在集群环境中的位置。
在可信网络环境下,TCP/IP协议的很多操作是冗余的,例如三次握手,增加了应用程序的通信时间,不利于对时间响应要求比较高的应用,比如 处理集群成员节点由于重启,down机等各种原因导致的增加和减少。 TIPC针对可信网络环境,减少了建立通信连接的步骤和寻址目标地址的操作 (在TCP/IP协议里, 完成这些操作节点间最少也需要9次包交换, 而使用TIPC则可以减少到2次)。这可以提高节点间信息交换的频率以及减少节点间等待的时间。
平时我们使用的socket,TCP也好,UDP也好,用来标识一对socket的通信,无非是用两个socket的IP地址和端口号,即五元组。比如使用UDP的socket,要发一个datagram到另一个socket,需要指定对端的地址,这个地址是由那台机的IP和端口组成。socket是在内核中管理,当内核检测到socket有数据可读时,就会通知拥有这个socket的进程去读取里面的数据。
这里的不方便之处在于,要指定对端地址,我们必须知道这个socket在哪台机,端口是多少,才能发送数据出去。能不能只提供一些应用层的信息,就可以让内核自己去查到socket的位置,再把消息发过去?TIPC做的就是这样的事。
使用TIPC,我们在创建socket的时候在内核中注册自己的服务类型service type,那么在发送端,只需要指定服务类型就可以由内核路由到相应的socket。这个时候,对应用层来说,对端地址仅仅是一个服务类型service type!很显然,内核维护着这么一张TIPC的路由表,即由服务去查找socket。而每台机都有这样的路由表,他们之间信息就像能够共享一样地为整个集群的TIPC socket服务。有了TIPC,这个socket使用了哪个IP,那个端口,我们都不再需要知道,很好很强大。
TIPC还具有如下特性:
有些时候多个进程提供同样的服务,仅仅是为了负载平衡或其他原因,这种情况可以用一个整数变量instance来标识不同socket,但是指定同样的service type。这个时候socket的地址是由service type和instance共同来指定。发送数据时候只需要指定service type和一个instance的值,也可以指定service type和instance的一个区间。对于后者,就是broadcast你的datagram。
管理前面说的TIPC路由表的是内核当中的一个进程叫做name server。它知晓着集群中所有的TIPC socket。在发送datagram给服务某个service的socket之前,你可以向它请求服务这个service的socket是否已经在工作了,它会告诉你service的状态。并且注册了一个observer,当你关心的socket起来之后发消息通知你,这样就可以避免你把datagram发给一个根本不存在的socket。
特点:网路中服务位置透明,自动发现机制,可靠性,标准套接字接口的支持,无连接,有链接,多播。
2 TIPC的socket接口分析
2.1 socket地址
socket地址是个16字节的结构,如下所示:
struct sockaddr { unsigned short sa_family; /* 地址家族, AF_xxx */ char sa_data[14]; /*14字节协议地址*/ };
为了编程可以更加方便通常我们会针对不同的协议定义不同的并行结构,如tipc的socket地址定义如下:
struct sockaddr_tipc { unsigned short family; /* AF_TIPC */ unsigned char addrtype; /* TIPC_ADDR_NAME */ signed char scope; /* TIPC_CLUSTER_SCOPE */ union { struct tipc_portid id; struct { unsigned long type; unsigned long instance; unsigned long domain; /* 0: own zone */ } name; } addr; };
2.2 tipc的地址
tipc的地址描述如下:port。Z表示zone, C表示cluster,N表示node。Port可以看成是一个socket
当启动tipc服务的时候,确定当前node(可以是一块单板,也可以是一台主机)的地址。我们创建一个socket就创建了一个port。Tipc提供名字服务,在sockaddr_tipc中指定name,把sockaddr_tipc和port绑定起来,向指定的名字发送消息就会发到对应的socket,而下层的则不需要用户去感知。名字和端口支持1对多的关系,一个名字对应多个端口
2.3 创建socket
int socket(int domain, int type, int protocol);这个函数的目的是创建一个socket,然后返回一个socket描述符。Domain在这里是AF_TIPC,type通常有SOCK_STREAM,SOCK_DGRAM两种,表示面向流或面向包。Protocol通常设置为0。
2.4 bind操作
一旦有一个socket,我们可能要将socket和机器上的某个地址关联起来,这个操作由bind来完成。
Bind和connect,原socket在应用bind的时候我们需要自己指定端口,connect则会自动为我们选定接口,但对于tipc应用层,不存在这种关系。(待确认)
2.5 getsockname
根据socket描述符获取当前的地址。
2.6 setsockopt
其参数如下:int sockfd, int level, int optname, const void *optval, socklen_t optlen
当level为SOL_TIPC,也就是使用tipc时,optname有如下值可以选择。
1) TIPC_IMPORTANCE
这个值用来标识本socket消息的重要性,设置为重要时本socket在发生拥塞时,消息丢失的可能性很小。 从下面的字面意思可以看出其含义,不再赘述。
TIPC_LOW_IMPORTANCE, 低优先级
TIPC_MEDIUM_IMPORTANCE, 中优先级
TIPC_HIGH_IMPORTANCE 高优先级
TIPC_CRITICAL_IMPORTANCE. 紧急优先级
默认是TIPC_LOW_IMPORTANCE
2)TIPC_SRC_DROPPABLE
同样是作为拥塞控制,如果设置为此值,则在拥塞发生时,tipc会丢弃消息,否则,将吧消息放入队列缓存。
默认情况下: 对SOCK_SEQPACKET, SOCK_STREAM, SOCK_RDM 三种传输方式,也就是可靠链接,则将消息缓存,对SOCK_DGRAM,也就是不可靠链接,将消息丢弃。
3)TIPC_DEST_DROPPABLE
仍然为拥塞控制服务。针对下面三种情况有用,消息不能发送到目的地址,或者目的地址不存在,或者目的地址发生了拥塞。如果使能这个功能,在发生以上三种情况是,消息将被丢弃,否则会将消息返回给发送者。
默认情况下:SOCK_SEQPACKET ,SOCK_STREAM两种传输方式,返回给发送者。
对SOCK_RDM and SOCK_DGRAM则将消息丢弃。这样做的目的是在在使用面向链接的情况下发生通信失败时进行合适的处理,同时不增加面向无链接的情况下通信失败的处理的复杂性。
3)TIPC_CONN_TIMEOUT
设置connect的超时时间,单位是毫秒
用sendto时,这个选项是没有意义的。
3 TIPC协议简介
3.1 协议基础
一些假设:
通过协议发送的大部分message都是直接到达目的地
大部分message的传输时间都很短
大部分message都在集群内部节点间传递
包丢失率很低, 重传不经常发生
可用带宽和内存都很大
所有带戳包的校验和都由硬件校验
通信节点的数量在一定时间内是相对受限和静态的
安全在封闭的集群环境里相对Internet来说不是关键因素
这些基础假定允许TIPC是一个基于流量驱动(traffic-driven)和固定大小滑动窗口的信号链路层协议。 而不是定时器驱动(timer-driven)的传输层协议。这使得TIPC拥有更早释放发送buffer, 更早侦测到包丢失并重传,更早侦测到节点不可用等优点。
3.2 TIPC网络结构
TIPC网络是由单个的处理单元或节点组成. 网络节点是严格分层的, 规则如下:
1:相关节点的集合构成一个cluster:
如果cluster中的每一个节点都至少有一条直达其他每个节点的路径(即cluster的节点是全连通的), 那么这些节点构成一个cluster, 每个cluster有1~4095个节点.
2:相关cluster的集合构成一个zone:
如果zone中的每一个cluster都至少有一条直达其他每个cluster的路径(即zone的cluster是全连通的), 那么这些cluster构成一个zone, 每个zone有1~4095个cluster, 每个cluster的大小不必相同.
3:相关zone的集合构成一个TIPC网络:
如果网络中的每一个zone都至少有一条直达其他每个zone的路径(即网络的zone是全连通的), 那么这些zone构成一个TIPC网络, 每个TIPC网络有1~255个zone, 每个zone的大小不必相同.
节点一般是按照邻近关系分组, TIPC的通信质量随着节点距离增加而下降,在一个典型的TIPC网络中,同一cluster中的节点间通信最频繁,其次是同一zone而不同cluster的节点,而不同zone中的节点基本没有通信。
TIPC网络地址是和整个物理结点相关联的,地址模式映射到逻辑网络拓扑。<z.c.n>方式。 Z(zone 8位),C(cluster, 12位), N(node, 12位), 共32位。 一个网络可包括255个域(zone)。zone:区域;cluster:集群;node:节点。
TIPC网络中的每个节点都有一个由zone ID,cluster ID和node ID组成的地址,一般标记为<Z.C.N>。其中: 1<=Z<=255,1<=C,N<=4095。如果一个TIPC网络节点还没有被分配地址,那么就以<0.0.0>标记它。TIPC网络一般也有自己的ID。这样可以使多个逻辑TIPC网络共同使用相同的物理介质(如以太网LAN等)而不相互干扰。
注意:
TIPC2.0只支持单cluster的网络。
TIPC网络节点的地址和IP有很大区别, 每个TIPC节点最多只有一个地址, 没有网络接口(interface)的概念。
每个节点的地址以及TIPC网络的ID都由网络管理员负责分配, 程序员不用关心这些。
4 Port
4.1 Port标识
Port 是TIPC网络协议的通信标识,具有网络唯一性,由运用程序创建port时由TIPC自动生成。
Port ID 典型标识是<z.c.n,:ref>, ref 是32位参考值,在node结点上是唯一的
Port ID属于TIPC网络通信的物理地址标识, 用于TIPC协议消息通信的内部路由。
4.2 Port name (功能地址)
Port name 形式 a{type, instance} #名字
B{type, lower, bound} #名字序列
Port name 和port ID 的映射是由TIPC的port 发布关联的,当应用bind指定的port name 时,TIPC自动生成关联port ID ,并在指定的网络区域发布。
5 消息发送
若node结点在本地,则直接挂消息到目的port队列,若非本地结点,则由可用的链路发到目的结点。
message是TIPC节点端口间信息交换的基本单元. TIPC中有2种基本消息:
payload message: 在应用程序和应用程序或应用程序和TIPC服务之间传送应用程序相关的内容.
internal message: 在TIPC子系统之间传送TIPC相关的内容.
每个TIPC消息都包含消息头部和数据2部分,消息头部的格式和用户相关, 大小从6个字到11字(word)不等(TIPC支持头部将来最大扩展到60字节)。头部是以网络字节序编码的32字节整形存储的。
6 常创建发消息失败的原因
a.长度大于66000字节,返回EINVAL;
b.TIPC用的内存不是使用的低端的内存,返回ENOMEM;
C.目的port链路阻塞,且发送端不期望阻塞,返回ELINKCONG;
d.基于有链接的链路对应的目的的port阻塞,且发送端不期望阻塞;
感谢:
TIPC协议 (含实验)
另附两篇最近发现的好文:
1, TIPC简介
TIPC是爱立信公司提出的一种透明进程间通信协议,
主要适用于高可用(HAL)和动态集群环境. 该软件当前主要由风河(windriver)公司在维护, 主要支持Linux, Solaris 和
VxWorks三种操作系统, 从Linux内核2.6.34开始支持TIPC的最新版本2.0, 不过还有很多协议设计的功能没有实现.
在可信网络环境下, TCP/IP协议的很多操作是冗余的, 例如, 著名的三次握手, 从而导致通信效率下降, 增加了应用程序的通信时间,
不利于对时间响应要求比较高的应用, 比如, 处理集群成员节点由于重启, down机等各种原因导致的增加和减少. TIPC针对可信网络环境,
减少了建立通信连接的步骤和寻址目标地址的操作(在TCP/IP协议里, 完成这些操作节点间最少也需要9次包交换,
而使用TIPC则可以减少到2次). 这可以提高节点间信息交换的频率以及减少节点间等待的时间.
2, TIPC协议简介
2.1, 协议基础
一些假设:
- 通过协议发送的大部分message都是直接到达目的地
- 大部分message的传输时间都很短
- 大部分message都在集群内部节点间传递
- 包丢失率很低, 重传不经常发生
- 可用带宽和内存都很大
- 所有带戳包的校验和都由硬件校验
- 通信节点的数量在一定时间内是相对受限和静态的
- 安全在封闭的集群环境里相对Internet来说不是关键因素
这些基础假定允许TIPC是一个基于流量驱动(traffic-driven)和固定大小滑动窗口的信号链路层协议. 而不是定时器驱动(timer-driven)的传输层协议. 这使得TIPC拥有更早释放发送buffer, 更早侦测到包丢失并重传, 更早侦测到节点不可用等优点.
2.2, TIPC体系结构视图
Node A Node B ------------- ------------- | TIPC | | TIPC | | Application | | Application | |-------------| |-------------| | | | | | TIPC |TIPC address TIPC address| TIPC | | | | | |-------------| |-------------| | L2 Bearer |Bearer address \/ Bearer address| L2 Bearer | | Service | /\ | Service | ------------- ------------- | | |---------------- Bearer Transport ----------------|
TIPC体系结构视图
2.3, TIPC网络结构
TIPC网络是由单个的处理单元或节点组成. 网络节点是严格分层的, 规则如下:
- 相关节点的集合构成一个cluster: 如果cluster中的每一个节点都至少有一条直达其他每个节点的路径(即cluster的节点是全连通的), 那么这些节点构成一个cluster, 每个cluster有1~4095个节点.
- 相关cluster的集合构成一个zone: 如果zone中的每一个cluster都至少有一条直达其他每个cluster的路径(即zone的cluster是全连通的), 那么这些cluster构成一个zone, 每个zone有1~4095个cluster, 每个cluster的大小不必相同.
- 相关zone的集合构成一个TIPC网络: 如果网络中的每一个zone都至少有一条直达其他每个zone的路径(即网络的zone是全连通的), 那么这些zone构成一个TIPC网络, 每个TIPC网络有1~255个zone, 每个zone的大小不必相同.
节点一般是按照邻近关系分组, TIPC的通信质量随着节点距离增加而下降, 在一个典型的TIPC网络中, 同一cluster中的节点间通信最频繁, 其次是同一zone而不同cluster的节点, 而不同zone中的节点基本没有通信.
TIPC网络中的每个节点都有一个由zone ID, cluster
ID和node ID组成的地址, 一般标记为<Z.C.N>. 其中: 1<=Z<=255, 1<=C,
N<=4095. 如果一个TIPC网络节点还没有被分配地址, 那么就以<0.0.0>标记它. TIPC网络一般也有自己的ID.
这样可以使多个逻辑TIPC网络共同使用相同的物理介质(如以太网LAN等)而不相互干扰.
------------------------------------------------------ ---------- | Zone <1> | | Zone <2> | | ----------------------- ---------------------- | | | | | Cluster <1.1> | | Cluster <1.2> | | | | | | | | | | | | | | ------- | | ------- ------- | | | | | | | | | | | | | | | | | | | | | Node | | | | Node +--+ Node | | | | | | | |<1.1.1>| ------- | | |<1.2.1>| |<1.2.2>| | | | | | | | +---+ | | | | | | | | | | | | | ---+--- | Node | |--| --+---- ------- | | | | | | | |<1.1.3>| | | | | | | | | | ---+--- | | | | --+-- | | | | | | | +---+ | | | |Seco.| | | | | | | | Node | ------- | | |<1.2.| | | | | | | |<1.1.2>| | | |3333>| | | | | | | | | | | ----- | | | | | | ------- | | | | | | | ----------------------- ---------------------- | | | | | | | ----------------------------------------------------- ----------
典型的TIPC网络拓扑图
注意:
- TIPC2.0只支持单cluster的网络.
- TIPC网络节点的地址和IP有很大区别, 每个TIPC节点最多只有一个地址, 没有网络接口(interface)的概念.
- 每个节点的地址以及TIPC网络的ID都由网络管理员负责分配, 程序员不用关心这些.
2.4, TIPC消息格式
message是TIPC节点端口间信息交换的基本单元. TIPC中有2种基本消息:
- payload message: 在应用程序和应用程序或应用程序和TIPC服务之间传送应用程序相关的内容.
- internal message: 在TIPC子系统之间传送TIPC相关的内容.
每个TIPC消息都包含消息头部和数据2部分, 消息头部的格式和用户相关, 大小从6个字到11字(word)不等(TIPC支持头部将来最大扩展到60字节). 头部是以网络字节序编码的32字节整形存储的.
1.TIPC协议概述
TIPC是爱立信开源的透明进程通信协议,一般用于集群系统中。
虽然tipc是基于socket实现的,但是与一般的socket还有所区别。平时我们使用socket,无论是TCP也好,UDP也好,用来标识一对socket的通信,无非是用两个socket的IP地址和端口号。比如使用UDP的socket,要发送一个datagram到另一个socket,需要指定对端的地址,这个地址是由对端设备的IP和端口号组成的。Socket是在内核中进行管理,当内核检测到socket有数据可读时,就会通知拥有这个socket的进程去读取数据。
这种实现由一种不方便,就是需要指定对端的地址,我们必须知道这个socket在哪台设备上,设备IP是多少,使用的端口号是什么,才能发送数据。
TIPC解决了这个问题。使用TIPC,我们在创建socket的时候,在内核中注册自己的服务类型,那么在发送端,只需要指定服务类型就可以由内核路由到相应的socket。这个时候,对应用层来讲,对端地址仅仅是一个服务类型。显然,内核维护着这样一张路由表,可以根据服务类型去找到对应的socket。每台设备都有这样的路由表,他们的信息就能够像普通路由表一样共享到整个集群网络中去,所有设备都可以进行socket查找。因此,有了TIPC,我们无需关心socket使用了哪个IP,哪个端口。
Tipc还具有如下特性:
- 有些时候,多个进程提供相同的服务,仅仅是为了负载均衡或冗余备份等原因,这种情况下可以用一个整数变量instance来标识不同的socket,但是指定同样的服务类型。此时,socket是由service type和instance共同指定的。发送数据的时候只需要指定service type和一个instance值即可。也可以指定service type和instance的一个区间,这种情况就是broadcast你的datagram
- 管理tipc路由表的是内核中的name server进程。他维护着集群中所有的tipc socket。在发送datagram给某个socket之前,可以向他请求
2.实验结果
本实验在Ubuntu 14.04环境中测试,两台虚拟机搭建集群环境
虚拟机操作系统需要先加载tipc模块,使用命令
modprobe tipc
编译并运行tipcutils,方便我们对tipc进行配置
tipcutils代码可以在github上获取https://github.com/parbhu/tipcutils/tree/96413b283861d271bff23f3098565da58536c628
或者
https://github.com/TIPC/tipcutils
2.1.集群配置
配置tipc的网卡和地址,配置成功之后,两台设备上都可以发现tipc邻居,具体结果如下图所示
此时就可以通过tipc进行通信了。
2.2.通信测试
以hello_world模块进行测试
- 1.1.2设备当成集群的服务端,运行server_tipc程序
- 1.1.3设备当成集群的客户端,运行client_tipc程序
//服务端可客户端定义应该一样 #define SERVER_TYPE 18888 #define SERVER_INST 17
服务端
根据参数创建socket之后,就一直处于监听socket,等待数据到来,并对数据进行处理
server_addr.family = AF_TIPC; server_addr.addrtype = TIPC_ADDR_NAMESEQ; server_addr.addr.nameseq.type = SERVER_TYPE; server_addr.addr.nameseq.lower = SERVER_INST; server_addr.addr.nameseq.upper = SERVER_INST; server_addr.scope = TIPC_ZONE_SCOPE; sd = socket(AF_TIPC, SOCK_RDM, 0); if (0 != bind(sd, (struct sockaddr *)&server_addr, sizeof(server_addr))) { printf("Server: failed to bind port name\n"); exit(1); } if (0 >= recvfrom(sd, inbuf, sizeof(inbuf), 0, (struct sockaddr *)&client_addr, &alen)) { perror("Server: unexpected message"); } printf("Server: Message received: %s !\n", inbuf); if (0 > sendto(sd, outbuf, strlen(outbuf)+1, 0, (struct sockaddr *)&client_addr, sizeof(client_addr))) { perror("Server: failed to send"); }
客户端
创建socket之后主动发送数据,监听socket
服务端调用wait_for_server来等待server连上
wait_for_server(SERVER_TYPE, SERVER_INST, 10000); sd = socket(AF_TIPC, SOCK_RDM, 0); server_addr.family = AF_TIPC; server_addr.addrtype = TIPC_ADDR_NAME; server_addr.addr.name.name.type = SERVER_TYPE; server_addr.addr.name.name.instance = SERVER_INST; server_addr.addr.name.domain = 0; if (0 > sendto(sd, buf, strlen(buf)+1, 0, (struct sockaddr*)&server_addr, sizeof(server_addr))) { perror("Client: failed to send"); exit(1); } if (0 >= recv(sd, buf, sizeof(buf), 0)) { perror("Client: unexpected response"); exit(1); }
运行结果
链接:https://www.jianshu.com/p/7fb89e3d19af
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
透明进程间通信(Transparent Inter-process Communication, TIPC) 是一种用于进程间通信的网络通信协议,原本是为集群间通信特别设计的。它允许设计人员能够创建可以和其它应用快速可靠地通信应用,无须考虑在其它需要通信的应用在集群环境中的位置。
特点:网路中服务位置透明,自动发现机制,可靠性,标准套接字接口的支持,无连接,有链接,多播。
在linux vxworks 可用这个协议,用C或者C++的运用中能创建AF-TIPC的SOCKET套接字来使用TIPC协议
二.TIPC模块的编译
1. TIPC 的网路功能是采用模块的形式在内核实现的,模块为tipc.ko
Linux
| ---include
| | -----linux |----tipc.h
| | |----tipc_config.h
| |
| | -----net -----tipc|----tipc.h
| |---tipc_plugin_if.h
| | ---tipc_plugin_msg.h
| | ---tipc_plugin_pert.h
|
| |-----konfig makefile
| ----net-----tipc |-----tipc_acldr.c
| -----tipc_user_reg.c
| -----tipc_user_reg.h
2.下载源文件,并将之替换掉当前内核中的对应位置的TIPC源文件
3.在linux源目录运行 make menuconfig 配置TIPC的参数
4.使用make modules M=net/tipc 编译tipc模块,若编译正确将在/usr/src/linux/net/tipc目录中生成tipc-*.ko 内核模块文件
5.安装 有以下命令:
1. lsmod |grep tipc
2.modinfo tipc.ko (查看新tipc文件内核版本)
3.rmmod tipc-* (删除系统中的版本)
4.insmod tipc.ko (插入新的TIPC版本)
三.接收发送
1.发送端实例
Struct sockaddr_tipc server_addr; Int sd = socket (AF_TIPC, SOCK_RDM, 0); Server.addr.family = AF_TIPC; Server.addr.assrtype = TIPC_ADDR_NAME; Server.addr.addr.name.type = type; //TIPC Server.addr.addr.name.name.instance = port; Server.addr.addr.name.domain = 0; Char buf[40] = {“hello world”}; If (0>sendto (sd buf,strlen(buf)+1,0, (struct sockaddr*)&server_addr, sizeof(server_addr))) { Perror(“client:failed send”); Exit(1); }
接受端实例:
Struct sockaddr_tipc server_addr; Int sd = socket (AF_TIPC, SOCK_RDM, 0); Server.addr.family = AF_TIPC; Server.addr.assrtype = TIPC_ADDR_NAME; Server.addr.addr.name.type = type; //TIPC Server.addr.addr.name.name.instance = port; Server.addr.addr.name.domain = 0; If (0 != bind(sd,(struct sockaddr*)&server_addr, sizeof(server_addr))) { Printf (“failed to bind ”); Eixt(1); } If(0>=recvfrom(sd, Inbuf, sizeof(inbuf), 0, (strruct sockaddr*)&client_addr, &alen)) { Peeror(“unexepected message”; } Printf(“sever:message receiived”, inbuf);
四.网络拓扑
TIPC网络地址是和整个物理结点相关联的,地址模式映射到逻辑网络拓扑。<z.c.n>方式。 Z(zone 8位),C(cluster, 12位), N(node, 12位), 共32位。 一个网络可包括255个域(zone),
一个域包括4096个集群(cluster),一个集群包括4096个结点(node).
TIPC采用5级分层网络: netnode, zone , cluster,node,slave node 0 zone内部每个node 可以互相通信。Networks则逻辑隔离
TIPC运用程序可以发送长1—66000字节长的消息,它的协议栈负责报文的分片和重组
TIPC体系结构视图
Node A Node B
------------- -------------
| TIPC | | TIPC |
| Application | | Application |
|-------------| |-------------|
| | | |
| TIPC |TIPC address TIPC address| TIPC |
| | | |
|-------------| |-------------|
| L2 Bearer |Bearer address \/ Bearer address| L2 Bearer |
| Service | /\ | Service |
------------- -------------
| |
|---------------- Bearer Transport ----------------|
TIPC网络结构
TIPC网络是由单个的处理单元或节点组成. 网络节点是严格分层的, 规则如下:
相关节点的集合构成一个cluster: 如果cluster中的每一个节点都至少有一条直达其他每个节点的路径(即cluster的节点是全连通的), 那么这些节点构成一个cluster, 每个cluster有1~4095个节点.
相关cluster的集合构成一个zone: 如果zone中的每一个cluster都至少有一条直达其他每个cluster的路径(即zone的cluster是全连通的), 那么这些cluster构成一个zone, 每个zone有1~4095个cluster, 每个cluster的大小不必相同.
相关zone的集合构成一个TIPC网络: 如果网络中的每一个zone都至少有一条直达其他每个zone的路径(即网络的zone是全连通的), 那么这些zone构成一个TIPC网络, 每个TIPC网络有1~255个zone, 每个zone的大小不必相同.
节点一般是按照邻近关系分组, TIPC的通信质量随着节点距离增加而下降, 在一个典型的TIPC网络中, 同一cluster中的节点间通信最频繁, 其次是同一zone而不同cluster的节点, 而不同zone中的节点基本没有通信.
TIPC网络中的每个节点都有一个由zone ID, cluster ID和node ID组成的地址, 一般标记为<Z.C.N>. 其中: 1<=Z<=255, 1<=C, N<=4095. 如果一个TIPC网络节点还没有被分配地址, 那么就以<0.0.0>标记它. TIPC网络一般也有自己的ID. 这样可以使多个逻辑TIPC网络共同使用相同的物理介质(如以太网LAN等)而不相互干扰.
------------------------------------------------------ ---------- | Zone <1> | | Zone <2> | | ----------------------- ---------------------- | | | | | Cluster <1.1> | | Cluster <1.2> | | | | | | | | | | | | | | ------- | | ------- ------- | | | | | | | | | | | | | | | | | | | | | Node | | | | Node +--+ Node | | | | | | | |<1.1.1>| ------- | | |<1.2.1>| |<1.2.2>| | | | | | | | +---+ | | | | | | | | | | | | | ---+--- | Node | |--| --+---- ------- | | | | | | | |<1.1.3>| | | | | | | | | | ---+--- | | | | --+-- | | | | | | | +---+ | | | |Seco.| | | | | | | | Node | ------- | | |<1.2.| | | | | | | |<1.1.2>| | | |3333>| | | | | | | | | | | ----- | | | | | | ------- | | | | | | | ----------------------- ---------------------- | | | | | | | ----------------------------------------------------- ----------
典型的TIPC网络拓扑图
注意:
TIPC2.0只支持单cluster的网络.
TIPC网络节点的地址和IP有很大区别, 每个TIPC节点最多只有一个地址, 没有网络接口(interface)的概念.
每个节点的地址以及TIPC网络的ID都由网络管理员负责分配, 程序员不用关心这些.
五.Port标识
Port 是TIPC网络协议的通信标识,具有网络唯一性,由运用程序创建port时由TIPC自动生成。
Port ID 典型标识是<z.c.n,:ref>, ref 是32位参考值,在node结点上是唯一的
Port ID属于TIPC网络通信的物理地址标识, 用于TIPC协议消息通信的内部路由。
六.Port name (功能地址)
Port name 形式 a{type, instance} #名字
B{type, lower, bound} #名字序列
Port name 和port ID 的映射是由TIPC的port 发布关联的,当应用bind指定的port name 时,TIPC自动生成关联port ID ,并在指定的网络区域发布。
七.消息发送
若node结点在本地,则直接挂消息到目的port队列,若非本地结点,则由可用的链路发到目的结点。
TIPC消息格式
message是TIPC节点端口间信息交换的基本单元. TIPC中有2种基本消息:
payload message: 在应用程序和应用程序或应用程序和TIPC服务之间传送应用程序相关的内容.
internal message: 在TIPC子系统之间传送TIPC相关的内容.
每个TIPC消息都包含消息头部和数据2部分, 消息头部的格式和用户相关, 大小从6个字到11字(word)不等(TIPC支持头部将来最大扩展到60字节). 头部是以网络字节序编码的32字节整形存储的.
八.常创建发消息失败的原因
a.长度大于66000字节,返回EINVAL;
b.TIPC用的内存不是使用的低端的内存,返回ENOMEM;
C.目的port链路阻塞,且发送端不期望阻塞,返回ELINKCONG;
d.基于有链接的链路对应的目的的port阻塞,且发送端不期望阻塞;
————————————————
版权声明:本文为CSDN博主「panamera12」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wteruiycbqqvwt/article/details/90237718
TIPC是爱立信的某个工程师弄出来的,后来开源了。这段时间我琢磨了一下,觉得这个玩意还真不错。TIPC是Transparent Interprocess Communication的缩写,即是进程间通信的一种协议,之所以冠之以Transparent,透明的,因为它发布了一层更为简洁实用的框架,让使用的人不再需要知道某个进程运行在哪一台机子上,就能够和与这个进程通信。TIPC本质上是用socket实现的,而且现在已经成为linux内核的一部分,足以见得是好东西。
平时我们使用的socket,TCP也好,UDP也好,用来标识一对socket的通信,无非是用两个socket的IP地址和端口号。比如使用UDP的socket,要发一个datagram到另一个socket,需要指定对端的地址,这个地址是由那台机的IP和端口组成。socket是在内核中管理,当内核检测到socket有数据可读时,就会通知拥有这个socket的进程去读取里面的数据。
这里的不方便之处在于,要指定对端地址,我们必须知道这个socket在哪台机,端口是多少,才能发送数据出去。能不能只提供一些应用层的信息,就可以让内核自己去查到socket的位置,再把消息发过去?TIPC做的就是这样的事。使用TIPC,我们在创建socket的时候在内核中注册自己的服务类型service
type,那么在发送端,只需要指定服务类型就可以由内核路由到相应的socket。这个时候,对应用层来说,对端地址仅仅是一个服务类型service
type!很显然,内核维护着这么一张TIPC的路由表,即由服务去查找socket。而每台机都有这样的路由表,他们之间信息就像能够共享一样地为整个集群的TIPC
socket服务。有了TIPC,这个socket使用了哪个IP,那个端口,我们都不再需要知道,很好很强大。
TIPC的其他的特性还有:
1.
有些时候多个进程提供同样的服务,仅仅是为了负载平衡或其他原因,这种情况可以用一个整数变量instance来标识不同socket,但是指定同样的service
type。这个时候socket的地址是由service type和instance共同来指定。发送数据时候只需要指定service
type和一个instance的值,也可以指定service
type和instance的一个区间。对于后者,就是broadcast你的datagram。
2.
管理前面说的TIPC路由表的是内核当中的一个进程叫做name server。它知晓着集群中所有的TIPC
socket。在发送datagram给服务某个service的socket之前,你可以向它请求服务这个service的socket是否已经在工作了,它会告诉你service的状态。并且注册了一个observer,当你关心的socket起来之后发消息通知你,这样就可以避免你把datagram发给一个根本不存在的socket。
TIPC安装及配置
- 编译内核:
步骤:
1.cp tipc-1.7.5.tar /usr/src/linux-2.6.16.21-0.8
2.tar -xvf tipc-1.7.5.tar
3.cd /usr/src/linux-2.6.16.21-0.8
4.进入/usr/src/linux-2.6.16.21-0.8/
5.make menuconfig
6.make
7.make bzImage
8.make modules
9.make modules_install
10.make install
- 安装配置:
步骤:
1.将ticp的压缩包放在/usr/src/linux-2.6.16.21-0.8下,
2. tar -xvf tipc-1.7.5.tar
3.cd /usr/src/linux-2.6.16.21-0.8/net/tipc
4. make (现在1.7.5已经提供提供了可以配置的选项,在make之前可以使用make menuconfig进行配置,默认配置也可以用。)
5.insmod. tipc.ko
6.编译tipc提供的util配置工具。
7. ./tipc-config -netid=1234 -a=1.1.5 配置本节点的id为1234 地址是1.1.5
8. ./tipc-config -be=eth:eth0/1.1.0 配置通信方式,通过本机的第一个以太网口进行通信。只向本集群内的节点进行通信。配置成1.1.8标识指向1.1.8节点进行通信,1.0.0表示向本zone内的各个集群都可以通信,0.0.0则表示向任何地方通信。
2,TIPC的socket接口分析:
l socket地址
socket地址是个16字节的结构,如下所示:
struct sockaddr
{
unsigned short sa_family; /* 地址家族, AF_xxx */
char sa_data[14]; /*14字节协议地址*/
};
为了编程可以更加方便通常我们会针对不同的协议定义不同的并行结构,如tipc的socket地址定义如下:
struct sockaddr_tipc {
unsigned short family; /* AF_TIPC */
unsigned char addrtype; /* TIPC_ADDR_NAME */
signed char scope; /* TIPC_CLUSTER_SCOPE */
union {
struct tipc_portid id;
struct {
unsigned long type;
unsigned long instance;
unsigned long domain; /* 0: own zone */
} name;
} addr;
};
l tipc的地址:
tipc的地址描述如下:port。Z表示zone, C表示cluster,N表示node。Port可以看成是一个socket
当启动tipc服务的时候,确定当前node(可以是一块单板,也可以是一台主机)的地址。我们创建一个socket就创建了一个port。Tipc提供名字服务,在sockaddr_tipc中指定name,把sockaddr_tipc和port绑定起来,向指定的名字发送消息就会发到对应的socket,而下层的则不需要用户去感知。名字和端口支持1对多的关系,一个名字对应多个端口
l 创建socket
int socket(int domain, int type, int protocol);这个函数的目的是创建一个socket,然后返回一个socket描述符。Domain在这里是AF_TIPC,type通常有SOCK_STREAM,SOCK_DGRAM两种,表示面向流或面向包。Protocol通常设置为0。
l bind操作。
一旦有一个socket,我们可能要将socket和机器上的某个地址关联起来,这个操作由bind来完成。
Bind和connect,原socket在应用bind的时候我们需要自己指定端口,connect则会自动为我们选定接口,但对于tipc应用层,不存在这种关系。
l getsockname,根据socket描述符获取当前的地址。
l setsockopt
其参数如下:int sockfd, int level, int optname, const void *optval, socklen_t optlen
当level为SOL_TIPC,也就是使用tipc时,optname有如下值可以选择。
1) TIPC_IMPORTANCE
这个值用来标识本socket消息的重要性,设置为重要时本socket在发生拥塞时,消息丢失的可能性很小。 从下面的字面意思可以看出其含义,不再赘述。
TIPC_LOW_IMPORTANCE, 低优先级
TIPC_MEDIUM_IMPORTANCE, 中优先级
TIPC_HIGH_IMPORTANCE 高优先级
TIPC_CRITICAL_IMPORTANCE. 紧急优先级
默认是TIPC_LOW_IMPORTANCE
2)TIPC_SRC_DROPPABLE
同样是作为拥塞控制,如果设置为此值,则在拥塞发生时,tipc会丢弃消息,否则,将吧消息放入队列缓存。
默认情况下: 对SOCK_SEQPACKET, SOCK_STREAM, SOCK_RDM 三种传输方式,也就是可靠链接,则将消息缓存,对SOCK_DGRAM,也就是不可靠链接,将消息丢弃。
3)TIPC_DEST_DROPPABLE
仍然为拥塞控制服务。针对下面三种情况有用,消息不能发送到目的地址,或者目的地址不存在,或者目的地址发生了拥塞。如果使能这个功能,在发生以上三种情况是,消息将被丢弃,否则会将消息返回给发送者。
默认情况下:SOCK_SEQPACKET ,SOCK_STREAM两种传输方式,返回给发送者。
对SOCK_RDM and SOCK_DGRAM则将消息丢弃。这样做的目的是在在使用面向链接的情况下发生通信失败时进行合适的处理,同时不增加面向无链接的情况下通信失败的处理的复杂性。
3)TIPC_CONN_TIMEOUT
设置connect的超时时间,单位是毫秒
用sendto时,这个选项是没有意义的。
编程示例:
service.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TEST_MSG_SIZE 2048
#define TEST_READ_TIMES 10
#define TEST_MSG_LEN 64*1024
typedef struct tagDmslLogiLinkAddrTipc
{
int uiType;
int uiInstance;
}TIPC_ADDR_STR;
int dmsGetSockAddr(struct sockaddr* SocketAddr, TIPC_ADDR_STR *pstLogilinkAdds)
{
struct sockaddr_tipc* addr = (struct sockaddr_tipc*)SocketAddr;
/*设定监听地址*/
if((0 == addr)||(0 == pstLogilinkAdds))
{
return 10000;
}
memset(addr, 0, sizeof(struct sockaddr_tipc));
addr->family = AF_TIPC;
addr->addrtype = TIPC_ADDR_NAME;
addr->addr.name.name.type = pstLogilinkAdds->uiType;
addr->addr.name.name.instance = pstLogilinkAdds->uiInstance;
addr->addr.name.domain = 0;
addr->scope = 2;
return 0;
}
int main()
{
int uiRet,stSocket,uiNum,n,i;
struct sockaddr location_addr, TagSockAddr;
struct sockaddr_tipc servAddr;
TIPC_ADDR_STR* pstPortName;
void *pMsg;
fd_set fdReadSet;
struct timeval stwait;
socklen_t addrlen = sizeof(struct sockaddr);
static int uiMsgNum = 0;
char Buf[TEST_MSG_SIZE] ;
struct msghdr stMsg;
struct iovec astIovec[1];
pstPortName = malloc(sizeof(TIPC_ADDR_STR));
pstPortName->uiType = 0x80000001;
pstPortName->uiInstance = 130987;
stSocket = socket(AF_TIPC, SOCK_RDM, 0);
if (-1 == stSocket)
{
perror("socket");
return -1;
}
uiRet = dmsGetSockAddr(&location_addr, pstPortName);
if(0 != uiRet)
{
printf("dmsGetSockAddr Failed 0x%x",uiRet);
return -1;
}
uiRet = bind(stSocket, (__CONST_SOCKADDR_ARG)&location_addr, (socklen_t)sizeof(location_addr));
if (0 != uiRet)
{
printf("bind Failed 0x%x",uiRet);
return -1;
}
pMsg = malloc(TEST_MSG_SIZE);
if (NULL == pMsg)
{
printf("malloc Failed");
return -1;
}
while(1)
{
FD_ZERO( &fdReadSet );
FD_SET(stSocket, &fdReadSet);
stwait.tv_sec = 0;
stwait.tv_usec = 10000;
// if(select(stSocket + 1, &fdReadSet, (fd_set*)0, (fd_set*)0, &stwait)>0)
{
// uiNum = 0;
// while (uiNum < TEST_READ_TIMES)
{
n = recvfrom(stSocket, pMsg, TEST_MSG_LEN, 0, ( __SOCKADDR_ARG)(struct
sockaddr_tipc *)&servAddr, (unsigned int *)(size_t*)&addrlen);
if (n <= 0)
{
continue;
}
// uiMsgNum += n / TEST_MSG_SIZE;
pstPortName->uiType = 0x70000001;
pstPortName->uiInstance = 130986;
uiRet = dmsGetSockAddr(&TagSockAddr, pstPortName);
if(0 != uiRet)
{
printf("dmsGetSockAddr Failed 0x%x",uiRet);
return -1;
}
// uiNum++;
// for(i = 0;i < uiMsgNum;i++)
{
// pBuf = malloc(TEST_MSG_SIZE);
// memset(pBuf, 0, TEST_MSG_SIZE);
sendto(stSocket, (void*)Buf, TEST_MSG_SIZE, 0,
(struct sockaddr*)&TagSockAddr, sizeof(TagSockAddr));
// printf("send msg num is %d\n",i);
}
}
}
}
return 0;
}
client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TEST_MSG_LEN 64*1024
#define TEST_MSG_SIZE 2048
typedef struct tagDmslLogiLinkAddrTipc
{
int uiType;
int uiInstance;
}TIPC_LOGI_ADDR_STR;
//typedef void *( * OSAL_LINUX_TASK_ENTRY )( VOS_VOID * );
int stSocket;
int dmsGetSockAddr(struct sockaddr* SocketAddr, TIPC_LOGI_ADDR_STR *pstLogilinkAdds)
{
struct sockaddr_tipc* addr = (struct sockaddr_tipc*)SocketAddr;
/*设定监听地址*/
if((0 == addr)||(0 == pstLogilinkAdds))
{
return -1;
}
memset(addr, 0, sizeof(struct sockaddr_tipc));
addr->family = AF_TIPC;
addr->addrtype = TIPC_ADDR_NAME;
addr->addr.name.name.type = pstLogilinkAdds->uiType;
addr->addr.name.name.instance = pstLogilinkAdds->uiInstance;
addr->addr.name.domain = 0;
addr->scope = 2;
return 0;
}
void RecvProc(void *pThis)
{
fd_set fdReadSet;
struct timeval stwait;
int uiNum,n,uiSecd;
static int uiMsgNum = 0;
static struct timeval stEndtimeFei,stStarttimeFei;
struct sockaddr_tipc servAddr;
socklen_t addrlen = sizeof(struct sockaddr);
void *pMsg;
printf("------------");
pMsg = malloc(TEST_MSG_SIZE);
if (NULL == pMsg)
{
printf("malloc Failed");
return ;
}
while(1)
{
FD_ZERO( &fdReadSet );
FD_SET(stSocket, &fdReadSet);
stwait.tv_sec = 0;
stwait.tv_usec = 10000;
// if(select(stSocket + 1, &fdReadSet, (fd_set*)0, (fd_set*)0, &stwait)>0)
{
// uiNum = 0;
// while (uiNum < TEST_READ_TIMES)
{
n = recvfrom(stSocket, pMsg, TEST_MSG_LEN, 0, ( __SOCKADDR_ARG)(struct
sockaddr_tipc *)&servAddr, (unsigned int *)(size_t*)&addrlen);
if (n <= 0)
{
continue;
}
if(uiMsgNum == 0)
{
gettimeofday(&stStarttimeFei,NULL);
}
// uiMsgNum += n / TEST_MSG_SIZE;
uiMsgNum++;
// printf("recv msg num is %d\n",uiMsgNum);
// uiNum++;
if((uiMsgNum%200000) == 0)
{
gettimeofday(&stEndtimeFei,NULL);
uiSecd = (stEndtimeFei.tv_sec - stStarttimeFei.tv_sec)*1000 +
(stEndtimeFei.tv_usec - stStarttimeFei.tv_usec)/1000 ;
printf("The flux is %u\n",200000/uiSecd);
gettimeofday(&stStarttimeFei,NULL);
}
}
}
}
}
int main(int ac, char **av)
{
pthread_attr_t attr; /* attributes for thread */
pthread_t tid;
void *pThis;
int uiRet,uiSize,i,j;
struct sockaddr location_addr, TagSockAddr;
TIPC_LOGI_ADDR_STR* pstPortName;
TIPC_LOGI_ADDR_STR* pstDstPortName;
char Buf[TEST_MSG_SIZE] ;
struct msghdr stMsg;
int iNum;
iNum = atoi(av[1])
uiSize = TEST_MSG_SIZE;
pstPortName = malloc(sizeof(TIPC_LOGI_ADDR_STR));
pstDstPortName = malloc(sizeof(TIPC_LOGI_ADDR_STR));
pstPortName->uiType = 0x70000001;
pstPortName->uiInstance = 130986;
stSocket = socket(AF_TIPC, SOCK_RDM, 0);
if (-1 == stSocket)
{
perror("socket");
exit(1);
}
uiRet = dmsGetSockAddr(&location_addr, pstPortName);
if(0 != uiRet)
{
printf("\r\ndmsGetSockAddr Failed 0x%x",uiRet);
return -1;
}
uiRet = bind(stSocket, (__CONST_SOCKADDR_ARG)&location_addr, (socklen_t)sizeof(location_addr));
if (0 != uiRet)
{
perror("bind");
exit(1);
}
int svErrNo = pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
uiRet = pthread_create(&tid, &attr, (void*)&RecvProc, pThis);
printf("\r\n %u",uiRet);
fflush(stdout);
pstDstPortName->uiType = 0x80000001;
pstDstPortName->uiInstance = 130987;
if (0 != dmsGetSockAddr(&TagSockAddr, pstDstPortName))
{
printf("\r\ndmsGetSockAddr Failed 0x%x",uiRet);
return -1;
}
// struct iovec astIovec[1];
for (j= 0; ;j++)
{
if(j%iNum != 0)
{
/* pBuf = malloc(uiSize);
memset(pBuf, 0, uiSize);*/
sendto(stSocket, (void*)Buf, uiSize, 0,
(struct sockaddr*)&TagSockAddr, sizeof(TagSockAddr));
/*当前只使用发送一个分片接口*/
//uiRet = sendto(stSocket, pBuf, uiSize, 0,
(__CONST_SOCKADDR_ARG)(struct sockaddr_tipc *)&TagSockAddr,
sizeof(TagSockAddr));
if (0>uiRet)
{
perror("sendmsg");
return 10000;
}
// printf("send num is %d",j);
}
else
{
usleep(4000);
}
}
return 0;
}