SCTP是一個較新的傳輸協(xié)議,于2000年在IETF得到標(biāo)準(zhǔn)化(TCP是在1981年標(biāo)準(zhǔn)化的)。它最初是為滿足不斷增長的ip電話市場設(shè)計的;具體地說,就是穿越因特網(wǎng)傳輸電話信令。
SCTP是一個可靠的面向消息的協(xié)議,在端點之間提供多個流,并為多宿提供傳輸級支持。
盡管SCTP和TCP之間存在一些本質(zhì)性的差別,然而SCTP的一到一(one-to-one)接口與TCP提供的應(yīng)用接口非常接近。這一點允許輕而易舉地移植應(yīng)用程序,不過沒法使用SCTP的某些高級特性。SCTP的一到多(one-to-many)接口提供了這些特性的完全支持,然而可能需要費時費力地重新編寫已有的應(yīng)用程序。對于使用SCTP開發(fā)的大多數(shù)新的應(yīng)用程序而言,推薦使用一到多接口。
SCTP套接口分為兩類:一到一套接口和一到多套接口。一到一套接口相應(yīng)于單獨一個SCTP關(guān)聯(lián)。這種映射類似于TCP套接口和TCP連接的對應(yīng)關(guān)系。對于一到多套接口,一個給定套接口上可以同時有多個活躍的SCTP關(guān)聯(lián)。這種映射類似于綁定了某個特定端口的UDP套接口能夠從若干個同時在發(fā)送數(shù)據(jù)的遠地UDP端點接收彼此交錯的數(shù)據(jù)報。
一到一形式
開發(fā)一到一形式的目的是方便移植現(xiàn)有TCP應(yīng)用程序到SCTP上。它提供的模型與http://www.CUOXin.com/nufangrensheng/p/3586562.html中介紹的幾乎一樣。以下是兩者之間必須搞清的差異,特別是在把現(xiàn)有TCP應(yīng)用程序移植到SCTP的這種形式上時:
(1)任何TCP套接口選項必須轉(zhuǎn)換成等效的SCTP套接口選項。兩個較常見的選項是TCP_NODELAY和TCP_MAXSEG,它們應(yīng)該映射成SCTP_NODELAY和SCTP_MAXSEG。
(2)SCTP保存消息邊界,因而應(yīng)用層消息邊界并非必需。
(3)有些TCP應(yīng)用進程使用半關(guān)閉來告知對端去往它的數(shù)據(jù)流已經(jīng)結(jié)束。移植這樣的應(yīng)用協(xié)議SCTP需要額外重寫應(yīng)用層協(xié)議,讓應(yīng)用進程在應(yīng)用數(shù)據(jù)流中告知對端該傳輸數(shù)據(jù)流已經(jīng)結(jié)束。
(4)send函數(shù)能夠以普通方式使用。
下圖為SCTP一到一形式的套接口典型用法的時間線圖:
一到一式SCTP套接口是一個類型為SOCK_STREAM,協(xié)議為IPPROTO_SCTP的網(wǎng)際套接口(即協(xié)議族為AF_INET或AF_INET6)。
一到多形式
一到多形式給應(yīng)用程序開發(fā)人員提供這樣的能力:編寫的服務(wù)器程序無需管理大量的套接口描述字。單個套接口描述字將代表多個關(guān)聯(lián),就像一個UDP套接口能夠從多個客戶接收消息那樣。在一到多式套接口上,用于標(biāo)識單個關(guān)聯(lián)的是一個關(guān)聯(lián)標(biāo)識(association identifier)。關(guān)聯(lián)標(biāo)識是一個類型為sctp_assoc_t的值,通常是一個整數(shù)。它是一個不透明的值;應(yīng)用進程不應(yīng)該使用不是由內(nèi)核早先給予的任何關(guān)聯(lián)標(biāo)識。一到多套接口的用戶應(yīng)該掌握以下幾點:
(1)當(dāng)一個客戶關(guān)閉其關(guān)聯(lián)時,其服務(wù)器也將自動關(guān)閉同一個關(guān)聯(lián),服務(wù)器主機內(nèi)核中不再有該關(guān)聯(lián)的狀態(tài)。
(2)可用于致使在四路握手的第3個或第4個分組中捎帶用戶數(shù)據(jù)的唯一辦法就是使用一到多形式。
(3)對于一個與它還沒有關(guān)聯(lián)存在的IP地址,任何以它為目的地址的sendto、sendmsg或sctp_sendmsg將導(dǎo)致嘗試主動打開,從而(如果成功的話)建立一個與該地址的新關(guān)聯(lián)。這種行為的發(fā)生與執(zhí)行分組發(fā)送的這個應(yīng)用進程是否曾調(diào)用過listen函數(shù)以請求被動打開無關(guān)。
(4)用戶必須使用sendto、sendmsg或sctp_sendmsg這3個分組發(fā)送函數(shù),而不能使用send或write這2個分組發(fā)送函數(shù),除非已經(jīng)使用sctp_peeloff函數(shù)從一個一到多形式套接口剝離出一個一到一式套接口。
(5)任何時候調(diào)用其中任何一個分組發(fā)送函數(shù)時,所用的宿地址是由系統(tǒng)在關(guān)聯(lián)建立階段選定的主宿地址,除非調(diào)用者在所提供的sctp_sndrcvinfo結(jié)構(gòu)中設(shè)置了MSG_ADDR_OVER標(biāo)志。為了提供這個結(jié)構(gòu),調(diào)用者必須使用伴隨輔助數(shù)據(jù)的sendmsg函數(shù),或者是sctp_sendmsg函數(shù)。
(6)關(guān)聯(lián)事件可能被開啟,因此要是應(yīng)用進程不希望接收到這些事件,就得使用SCTP_EVENTS套接口選項顯式禁止它們。缺省情況下開啟的唯一事件是sctp_data_io_event,它給recvmsg和sctp_recvmsg調(diào)用提供輔助數(shù)據(jù)。這個缺省設(shè)置同時適用于一到一形式和一到多形式。
下圖為一到多套接口典型用法的時間線圖:
一到多式SCTP套接口是一個類型為SOCK_SEQPACKET,協(xié)議為IPPROTO_SCTP的網(wǎng)際套接口(即協(xié)議族為AF_INET或AF_INET6)。
在SCTP中,一個一到多套接口也能夠結(jié)合使用sctp_peeloff函數(shù)以允許組合迭代服務(wù)器模型和并發(fā)服務(wù)器模型:
(1)sctp_peeloff函數(shù)用于從一個一到多套接口剝離出某個特定的關(guān)聯(lián)(例如一個長期持續(xù)的會話),獨自構(gòu)成一個一到一式套接口。
(2)剝離出的關(guān)聯(lián)所在的一到一套接口隨后就可以遣送給它自己的線程,或者遣送給為它派生的進程(就像在并發(fā)模型中那樣)。
(3)與此同時,主線程繼續(xù)在原來的套接口上以迭代方式處理來自任何剩余關(guān)聯(lián)的消息。
SCTP服務(wù)器可能希望捆綁與所在主機系統(tǒng)相關(guān)IP地址的一個子集。傳統(tǒng)意義上,TCP服務(wù)器或UDP服務(wù)器要么捆綁所在主機的某個地址,要么捆綁所有地址,而不能捆綁這些地址的一個子集。sctp_bindx函數(shù)允許SCTP套接口捆綁一個特定的地址子集。
#include <netinet/sctp.h>int sctp_bindx(int sockfd, const struct sockaddr *addrs, int addrcnt, int flags);返回值:0——成功,-1——出錯
sockfd是由socket函數(shù)返回的套接口描述字。
addrs是一個指向緊湊的地址清單的指針。每個套接口地址結(jié)構(gòu)緊跟在前一個套接口地址結(jié)構(gòu)之后,中間沒有填充字節(jié)。如下圖所示:
傳遞給sctp_bindx的地址個數(shù)由addrcnt參數(shù)指定。
flags參數(shù)指導(dǎo)sctp_bindx調(diào)用執(zhí)行如下表所示的兩種行為之一:
flags | 說明 |
SCTP_BINDX_ADD_ADDR SCTP_BINDX_REM_ADDR | 把地址加入套接口中 從套接口中移除地址 |
sctp_bindx既可用于已綁定的套接口,也可用于未綁定的套接口。
對于未綁定的套接口,sctp_bindx調(diào)用將把給定的地址集合捆綁到其上。對于已綁定的套接口,若指定SCTP_BINDX_ADD_ADDR則把額外的地址加入到套接口描述字,若指定SCTP_BINDX_REM_ADDR則從套接口描述字的已加入地址中移除給定的地址。
如果在一個監(jiān)聽套接口上執(zhí)行sctp_bindx調(diào)用,那么將來產(chǎn)生的關(guān)聯(lián)將使用新的地址配置;已經(jīng)存在的關(guān)聯(lián)不受影響。
傳遞給sctp_bindx的兩個標(biāo)志是互斥的;如果同時指定,調(diào)用就會失敗,返回錯誤碼為EINVAL。
所有套接口地址結(jié)構(gòu)的端口號必須相同,而且必須與已經(jīng)綁定的端口號相匹配;否則調(diào)用就會失敗,返回EINVAL錯誤碼。
#include <netinet/sctp.h>int sctp_connectx(int sockfd, const struct sockaddr *addrs, int addrcnt);返回值:0——成功,-1——出錯
sctp_connectx函數(shù)用于連接到一個多宿對端主機。該函數(shù)在addrs參數(shù)中指定addrcnt個全部屬于同一對端的地址。addrs參數(shù)是一個緊湊的地址列表。SCTP棧使用其中一個或多個地址建立關(guān)聯(lián)。列在addrs參數(shù)中的所有地址都被認為是有效的經(jīng)過證實的地址。
getpeername函數(shù)不是為支持多宿概念的傳輸協(xié)議設(shè)計的;當(dāng)用于SCTP時它僅僅返回主宿地址。如果需要知道對端的所有地址,那么應(yīng)該使用sctp_getpaddrs函數(shù)。
#include <netinet/sctp.h>int sctp_getpaddrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs);返回值:存放在addrs中的對端地址數(shù),-1——出錯
sockfd參數(shù)是由socket函數(shù)返回的套接口描述字。
id參數(shù)對于一到多式套接口是它的關(guān)聯(lián)標(biāo)識;對于一到一套接口則被忽略。
addrs參數(shù)是一個地址指針,而地址內(nèi)容是由本函數(shù)動態(tài)分配并填入的緊湊的地址清單。用完之后,調(diào)用者使用sctp_freepaddrs釋放所分配的資源。
sctp_freepaddrs函數(shù)釋放由sctp_getpaddrs函數(shù)分配的資源。
#include <netinet/sctp.h>void sctp_freepaddrs(struct sockaddr **addrs);
addrs參數(shù)是指向由sctp_getpaddrs返回的地址數(shù)組的指針。
sctp_getladdrs函數(shù)用于獲取屬于某個關(guān)聯(lián)的本地地址。當(dāng)需要知道一個本地端點究竟在使用哪些本地地址時(它們可能是主機所有地址的某個子集),可以調(diào)用本函數(shù)。
#include <netinet/sctp.h>int sctp_getladdrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs);返回值:存放在addrs中的本端地址數(shù),-1——出錯
sockfd參數(shù)是由socket函數(shù)返回的套接口描述字。
id參數(shù)對于一到多式套接口是它的關(guān)聯(lián)標(biāo)識;對于一到一套接口則被忽略。
addrs參數(shù)是一個地址指針,而地址內(nèi)容是由本函數(shù)動態(tài)分配并填入的緊湊的地址清單。用完之后,調(diào)用者使用sctp_freeladdrs釋放所分配的資源。
sctp_freeladdrs函數(shù)釋放由sctp_getladdrs函數(shù)分配的資源。
#include <netinet/sctp.h>void sctp_freeladdrs(struct sockaddr **addrs);
addrs參數(shù)是指向由sctp_getladdrs返回的地址數(shù)組的指針。
ssize_t sctp_sendmsg(int sockfd, const void *msg, size_t msgsz, const struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream, uint32_t timetolive, uint32_t context);返回值:寫出的字節(jié)數(shù),-1——出錯
sockfd參數(shù)是由socket函數(shù)返回的套接口描述字。
msg參數(shù)指向一個msgsz字節(jié)長度的緩沖區(qū),其中內(nèi)容將發(fā)送給對端端點to。tolen參數(shù)指定存放在to中的地址長度。
ppid參數(shù)指定將隨數(shù)據(jù)塊傳遞的凈荷協(xié)議標(biāo)識符。
flags參數(shù)將傳遞給SCTP棧,用以標(biāo)識任何SCTP選項。
調(diào)用者在stream參數(shù)中指定一個SCTP流號。
調(diào)用者可以在timetolive參數(shù)中以毫秒為單位指定消息的生命期,其中0表示無限生命期。
context參數(shù)用于指定可能有的用戶上下文。
ssize_t sctp_recvmsg(int sockfd, void *msg, size_t msgsz, struct sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags);返回值:讀入的字節(jié)數(shù),-1——出錯
注意,如果應(yīng)用進程想要接收sctp_sndrcvinfo信息,那么必須使用SCTP_EVENTS套接口選項預(yù)定sctp_data_io_event(缺省情況下開啟)。
本函數(shù)調(diào)用返回時,msg參數(shù)所指緩沖區(qū)中被填入最多msgsz字節(jié)數(shù)的數(shù)據(jù)。消息發(fā)送者的地址存放在from參數(shù)中,地址結(jié)構(gòu)大小存放在fromlen參數(shù)中。msg_flags參數(shù)中存放可能有的消息標(biāo)志。注意,如果實現(xiàn)把sctp_recvmsg映射成recvmsg函數(shù),那么recvmsg的flags參數(shù)被設(shè)為0.
sctp_opt_info函數(shù)是為getsockopt函數(shù)無法支持SCTP的那些實現(xiàn)提供的。
int sctp_opt_info(int sockfd, sctp_assoc_t assoc_id, int opt, void *arg, socklen_t *siz);返回:0——成功,-1——出錯
sockfd參數(shù)給出獲取其上套接口選項信息的套接口描述字。
assoc_id參數(shù)給出可能存在的關(guān)聯(lián)標(biāo)識。
opt參數(shù)是SCTP的套接口選項。
arg給出套接口選項參數(shù)。
siz是一個socklen_t類型指針,用于存放參數(shù)的大小。
int sctp_peeloff(int sockfd, sctp_assoc_t id);返回:新的套接口描述字——成功,-1——出錯
其語義很像帶有一個額外參數(shù)的accept函數(shù)。調(diào)用者把一到多式套接口的sockfd和待抽取的關(guān)聯(lián)標(biāo)識id傳遞給函數(shù)調(diào)用。調(diào)用結(jié)束時將返回一個新的套接口描述字,它是一個與所請求關(guān)聯(lián)對應(yīng)的一到一式套接口描述字。
SCTP為應(yīng)用程序提供了多種可用的通知。SCTP用戶可以經(jīng)由這些通知追蹤相關(guān)關(guān)聯(lián)的狀態(tài)。通知傳遞的是傳輸級的事件,包括網(wǎng)絡(luò)狀態(tài)變動、關(guān)聯(lián)啟動、遠地運作錯誤以及消息不可遞送。不論是一到一式接口還是一到多式接口,缺省情況下除sctp_data_io_event以外的所有事件都是被禁止的。
使用SCTP_EVENTS套接口選項可以預(yù)訂8個事件。其中7個事件產(chǎn)生稱為通知(notification)的額外數(shù)據(jù),通知本身可經(jīng)由普通的套接口描述字獲取。當(dāng)產(chǎn)生它們的事件發(fā)生時,這些通知內(nèi)嵌在數(shù)據(jù)中加入到套接口描述字。在預(yù)訂相應(yīng)通知的前提下讀取某個套接口時,用戶數(shù)據(jù)和通知將在套接口緩沖區(qū)中交錯出現(xiàn)。為了區(qū)分來自對端的數(shù)據(jù)和由事件產(chǎn)生的通知,用戶應(yīng)該使用recvmsg函數(shù)或sctp_recvmsg函數(shù)。如果返回的數(shù)據(jù)是一個事件通知,那么這兩個函數(shù)返回的msg_flags參數(shù)將含有MSG_NOTIFICATION標(biāo)志。這個標(biāo)志告訴應(yīng)用進程剛剛讀入的消息不是來自對端的數(shù)據(jù),而是來自本地SCTP棧的一個通知。
新聞熱點
疑難解答