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;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章