tipc协议详解

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网络是由单个的处理单元或节点组成. 网络节点是严格分层的, 规则如下: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网络中的每个节点都有一个由zone ID, cluster ID和node ID组成的地址, 一般标记为. 其中: 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网络拓扑图注意: 1. TIPC2.0只支持单cluster的网络.2. TIPC网络节点的地址和IP有很大区别, 每个TIPC节点最多只有一个地址, 没有网络接口(interface)的概念.3. 每个节点的地址以及TIPC网络的ID都由网络管理员负责分配, 程序员不用关心这些.2.4, TIPC消息格式message是TIPC节点端口间信息交换的基本单元. TIPC中有2种基本消息:1. payload message: 在应用程序和应用程序或应用程序和TIPC服务之间传送应用程序相关的内容.2. internal message: 在TIPC子系统之间传送TIPC相关的内容.每个TIPC消息都包含消息头部和数据2部分, 消息头部的格式和用户相关, 大小从6个字到11字(word)不等(TIPC支持头部将来最大扩展到60字节). 头部是以网络字节序编码的32字节整形存储的. 说实话,接触TIPC也不是一天两天了,在TSP的时候就和它天天打交道。今天好好来研究一下。TIPC sourceforge site:http://tipc.sourceforge.net/index.shtml转载自:http://blog.chinaunix.net/uid-20205875-id-3042255.htmlTIPC是爱立信的某个工程师弄出来的,后来开源了。这段时间我琢磨了一下,觉得这个玩意还真不错。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.82.tar -xvf tipc-1.7.5.tar3.cd /usr/src/linux-2.6.16.21-0.84.进入/usr/src/linux-2.6.16.21-0.8/5.make menuconfig 6.make7.make bzImage8.make modules9.make modules_install10.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/tipc4. 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接口分析:  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;}; tipc的地址:tipc的地址描述如下:port。Z表示zone, C表示cluster,N表示node。Port可以看成是一个socket当启动tipc服务的时候,确定当前node(可以是一块单板,也可以是一台主机)的地址。我们创建一个socket就创建了一个port。Tipc提供名字服务,在sockaddr_tipc中指定name,把sockaddr_tipc和port绑定起来,向指定的名字发送消息就会发到对应的socket,而下层的则不需要用户去感知。名字和端口支持1对多的关系,一个名字对应多个端口  创建socketint socket(int domain, int type, int protocol);这个函数的目的是创建一个socket,然后返回一个socket描述符。Domain在这里是AF_TIPC,type通常有SOCK_STREAM,SOCK_DGRAM两种,表示面向流或面向包。Protocol通常设置为0。  bind操作。一旦有一个socket,我们可能要将socket和机器上的某个地址关联起来,这个操作由bind来完成。Bind和connect,原socket在应用bind的时候我们需要自己指定端口,connect则会自动为我们选定接口,但对于tipc应用层,不存在这种关系。  getsockname,根据socket描述符获取当前的地址。  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*1024typedef 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 2048typedef 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;}

你可能感兴趣的:(linux,linux-network,linux网络编程)