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