1. 簡介
SCTP是爲了在IP網上傳輸信令而由IETF的信令傳輸工做組(SIGTRAN)提出的傳輸層協議(RFC2960,RFC4960)。 和TCP,UDP相比, UDP是無鏈接的傳輸協議,它能知足低延遲的要求,可是它卻沒法保證可靠傳輸。TCP能保證數據可靠傳輸,可是它也不能徹底符合信令傳輸的要求;TCP套接字不支持多宿性;TCP是面向比特流的,將數據傳輸看成是沒有結構的字節序列。
2. SCTP的基本概念
編程
多宿是指一個SCTP 端點能夠經過多個IP地址到達,這樣兩個SCTP端點在創建了關聯後,數據能夠經過不 同的物理通路進行傳送。也就是說,當一條通路壞掉後,能夠經過另外一條通路到達對端。安全
因爲採用多個流進行傳輸並且各個流相互獨立,這樣當一個流中的數據包須要重傳,其餘流中的數據能夠 繼續傳輸, 解決了在TCP單流中容易出現的隊頭阻塞現象(head-of-line).服務器
SCTP採用「四次握手」的鏈接創建方式和COOKIE機制消除了SYN攻擊的威脅, Cookie機制設立的主要用意 是將狀態信息存儲在客戶端或者網絡上,而非服務器內存中,它的使用將服務器資源預留的時間推遲到了
Cookie帶回完整的鑑別信息後。這是一種簡單有效的防護DoS攻擊的方法。
3. SCTP編程
Linux內核從2.6已經支持SCTP協議棧了,並且也提供了套接口(socket), SCTP的套接口兩類:一對一(相似TCP)和一對多(相似UDP)。
網絡
而後客戶端能夠用connect()鏈接服務器, write(), read()讀寫,close()關閉套接口
服務器端用bind()綁定端口,listen()監聽,accept()接受鏈接,write()/read()讀寫,
close() 關閉,這和普通TCP程序是相同的。
socket
一對多方式的SCTP編程和UDP相似,打開的是SCTP的有序分組接口:
socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)
用的socket(), bin(), listen(), close()等函數和原來同樣,但發送接收數據是用
sctp_sendto(),sctp_sendmsg()和sctp_recvmsg()這些SCTP專用函數。
4. 例子
爲了使用方便使用SCTP的套接口,以下的文件須要安裝:
rpm -ivh lksctp-tools-1.0.7-1.i386.rpm
能夠用wget在http://downloads.sourceforge.net/lksctp下面download.
客戶端源代碼(sctpclnt.c):
tcp
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <arpa/inet.h> #include "common.h"
int main() { int connSock, in, i, ret, flags; struct sockaddr_in servaddr; struct sctp_status status; struct sctp_sndrcvinfo sndrcvinfo; struct sctp_event_subscribe events; struct sctp_initmsg initmsg; char buffer[MAX_BUFFER+1];
/* Create an SCTP TCP-Style Socket */ connSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Specify that a maximum of 5 streams will be available per socket */ memset( &initmsg, 0, sizeof(initmsg) ); initmsg.sinit_num_ostreams = 5; initmsg.sinit_max_instreams = 5; initmsg.sinit_max_attempts = 4; ret = setsockopt( connSock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg) );
/* Specify the peer endpoint to which we'll connect */ bzero( (void *)&servaddr, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(MY_PORT_NUM); servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
/* Connect to the server */ ret = connect( connSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
/* Enable receipt of SCTP Snd/Rcv Data via sctp_recvmsg */ memset( (void *)&events, 0, sizeof(events) ); events.sctp_data_io_event = 1; ret = setsockopt( connSock, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events) );
/* Read and emit the status of the Socket (optional step) */ in = sizeof(status); ret = getsockopt( connSock, SOL_SCTP, SCTP_STATUS, (void *)&status, (socklen_t *)&in );
printf("assoc id = %d\n", status.sstat_assoc_id ); printf("state = %d\n", status.sstat_state ); printf("instrms = %d\n", status.sstat_instrms ); printf("outstrms = %d\n", status.sstat_outstrms );
/* Expect two messages from the peer */
for (i = 0 ; i < 2 ; i++) {
in = sctp_recvmsg( connSock, (void *)buffer, sizeof(buffer), (struct sockaddr *)NULL, 0, &sndrcvinfo, &flags );
if (in > 0) { buffer[in] = 0; if (sndrcvinfo.sinfo_stream == LOCALTIME_STREAM) { printf("(Local) %s\n", buffer); } else if (sndrcvinfo.sinfo_stream == GMT_STREAM) { printf("(GMT ) %s\n", buffer); } }
}
/* Close our socket and exit */ close(connSock);
return 0; }
函數 |
服務器端源代碼(sctpsrvr.c):
oop
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/sctp.h> #include "common.h"
int main() { int listenSock, connSock, ret; struct sockaddr_in servaddr; struct sctp_initmsg initmsg; char buffer[MAX_BUFFER+1]; time_t currentTime;
/* Create SCTP TCP-Style Socket */ listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Accept connections from any interface */ bzero( (void *)&servaddr, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons(MY_PORT_NUM);
ret = bind( listenSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
/* Specify that a maximum of 5 streams will be available per socket */ memset( &initmsg, 0, sizeof(initmsg) ); initmsg.sinit_num_ostreams = 5; initmsg.sinit_max_instreams = 5; initmsg.sinit_max_attempts = 4; ret = setsockopt( listenSock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg) );
/* Place the server socket into the listening state */ listen( listenSock, 5 );
/* Server loop... */ while( 1 ) {
/* Await a new client connection */ printf("Awaiting a new connection\n"); connSock = accept( listenSock, (struct sockaddr *)NULL, (int *)NULL );
/* New client socket has connected */
/* Grab the current time */ currentTime = time(NULL);
/* Send local time on stream 0 (local time stream) */ snprintf( buffer, MAX_BUFFER, "%s\n", ctime(¤tTime) ); ret = sctp_sendmsg( connSock, (void *)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, LOCALTIME_STREAM, 0, 0 );
/* Send GMT on stream 1 (GMT stream) */ snprintf( buffer, MAX_BUFFER, "%s\n", asctime( gmtime( ¤tTime ) ) ); ret = sctp_sendmsg( connSock, (void *)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, GMT_STREAM, 0, 0 );
/* Close the client connection */ close( connSock );
}
return 0; }
spa |
分別編譯sctpclnt.c, sctpsrvr.c:
gcc -Wall -o sctpclnt sctpclnt.c -lsctp
gcc -Wall -o sctpsrvr sctpsrvr.c -lsctp
分別運行sctpsrvr和sctpclnt, 使用ethereal(wireshark)或者tcpdump來抓包。附件就是我抓的IP數據包。.net
1. 簡介
SCTP是爲了在IP網上傳輸信令而由IETF的信令傳輸工做組(SIGTRAN)提出的傳輸層協議(RFC2960,RFC4960)。 和TCP,UDP相比, UDP是無鏈接的傳輸協議,它能知足低延遲的要求,可是它卻沒法保證可靠傳輸。TCP能保證數據可靠傳輸,可是它也不能徹底符合信令傳輸的要求;TCP套接字不支持多宿性;TCP是面向比特流的,將數據傳輸看成是沒有結構的字節序列。
2. SCTP的基本概念
多宿是指一個SCTP 端點能夠經過多個IP地址到達,這樣兩個SCTP端點在創建了關聯後,數據能夠經過不 同的物理通路進行傳送。也就是說,當一條通路壞掉後,能夠經過另外一條通路到達對端。
因爲採用多個流進行傳輸並且各個流相互獨立,這樣當一個流中的數據包須要重傳,其餘流中的數據能夠 繼續傳輸, 解決了在TCP單流中容易出現的隊頭阻塞現象(head-of-line).
CTP採用「四次握手」的鏈接創建方式和COOKIE機制消除了SYN攻擊的威脅, Cookie機制設立的主要用意 是將狀態信息存儲在客戶端或者網絡上,而非服務器內存中,它的使用將服務器資源預留的時間推遲到了
Cookie帶回完整的鑑別信息後。這是一種簡單有效的防護DoS攻擊的方法。
3. SCTP編程
Linux內核從2.6已經支持SCTP協議棧了,並且也提供了套接口(socket), SCTP的套接口兩類:一對一(相似TCP)和一對多(相似UDP)。
而後客戶端能夠用connect()鏈接服務器, write(), read()讀寫,close()關閉套接口
服務器端用bind()綁定端口,listen()監聽,accept()接受鏈接,write()/read()讀寫,
close() 關閉,這和普通TCP程序是相同的。
一對多方式的SCTP編程和UDP相似,打開的是SCTP的有序分組接口:
socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)
用的socket(), bin(), listen(), close()等函數和原來同樣,但發送接收數據是用
sctp_sendto(),sctp_sendmsg()和sctp_recvmsg()這些SCTP專用函數。
4. 例子
爲了使用方便使用SCTP的套接口,以下的文件須要安裝:
rpm -ivh lksctp-tools-1.0.7-1.i386.rpm
能夠用wget在http://downloads.sourceforge.net/lksctp下面download.
客戶端源代碼(sctpclnt.c):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <arpa/inet.h> #include "common.h"
int main() { int connSock, in, i, ret, flags; struct sockaddr_in servaddr; struct sctp_status status; struct sctp_sndrcvinfo sndrcvinfo; struct sctp_event_subscribe events; struct sctp_initmsg initmsg; char buffer[MAX_BUFFER+1];
/* Create an SCTP TCP-Style Socket */ connSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Specify that a maximum of 5 streams will be available per socket */ memset( &initmsg, 0, sizeof(initmsg) ); initmsg.sinit_num_ostreams = 5; initmsg.sinit_max_instreams = 5; initmsg.sinit_max_attempts = 4; ret = setsockopt( connSock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg) );
/* Specify the peer endpoint to which we'll connect */ bzero( (void *)&servaddr, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(MY_PORT_NUM); servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
/* Connect to the server */ ret = connect( connSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
/* Enable receipt of SCTP Snd/Rcv Data via sctp_recvmsg */ memset( (void *)&events, 0, sizeof(events) ); events.sctp_data_io_event = 1; ret = setsockopt( connSock, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events) );
/* Read and emit the status of the Socket (optional step) */ in = sizeof(status); ret = getsockopt( connSock, SOL_SCTP, SCTP_STATUS, (void *)&status, (socklen_t *)&in );
printf("assoc id = %d\n", status.sstat_assoc_id ); printf("state = %d\n", status.sstat_state ); printf("instrms = %d\n", status.sstat_instrms ); printf("outstrms = %d\n", status.sstat_outstrms );
/* Expect two messages from the peer */
for (i = 0 ; i < 2 ; i++) {
in = sctp_recvmsg( connSock, (void *)buffer, sizeof(buffer), (struct sockaddr *)NULL, 0, &sndrcvinfo, &flags );
if (in > 0) { buffer[in] = 0; if (sndrcvinfo.sinfo_stream == LOCALTIME_STREAM) { printf("(Local) %s\n", buffer); } else if (sndrcvinfo.sinfo_stream == GMT_STREAM) { printf("(GMT ) %s\n", buffer); } }
}
/* Close our socket and exit */ close(connSock);
return 0; }
|
服務器端源代碼(sctpsrvr.c):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/sctp.h> #include "common.h"
int main() { int listenSock, connSock, ret; struct sockaddr_in servaddr; struct sctp_initmsg initmsg; char buffer[MAX_BUFFER+1]; time_t currentTime;
/* Create SCTP TCP-Style Socket */ listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Accept connections from any interface */ bzero( (void *)&servaddr, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons(MY_PORT_NUM);
ret = bind( listenSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
/* Specify that a maximum of 5 streams will be available per socket */ memset( &initmsg, 0, sizeof(initmsg) ); initmsg.sinit_num_ostreams = 5; initmsg.sinit_max_instreams = 5; initmsg.sinit_max_attempts = 4; ret = setsockopt( listenSock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg) );
/* Place the server socket into the listening state */ listen( listenSock, 5 );
/* Server loop... */ while( 1 ) {
/* Await a new client connection */ printf("Awaiting a new connection\n"); connSock =
|