2009年5月25日 星期一

如何使用Sctp的partial reliability

因為要寫一個handover的網路電話,所以必需要知道目前使用的線路可能快要有問題了, 必需要切換線路,其中一個方式就是使用SCTP_SEND_FAILED的這一個事件。

SCTP_SEND_FAILED的介紹
When a message can't be delivered to a peer, the message is sent back to the user through this notification.
This notification is usually soon followed by an association failure notification.

基本上會出現SCTP_SEND_FAILED只有以下兩個情況
1. In most cases, the only way a message will not be delivered is if the association has failed.
只有當目前的這一個association才會顯示SCTP_SEND_FAILED
2. The only time a message failure will occur without an association failure is when the partial reliability extension of SCTP is being used
當有使用partial reliability時,若沒有在限定的時間(TTL[time to live])內送出去的話, 也會觸發SCTP_SEND_FAILED的事件

研究了好久, 總算知道如何使用partial reliability啦~
這裡使用的是one-to-one 的sctp, 也就是tcp style 的sctp,
one-to-many是不是也是一樣的設定方式,目前還不知道

這裡我們把它分為sender端與receiver端
sender端:
1. 在accept後, 要設定由accept回傳的connfd為SCTP_NODELAY, 因為若沒有設定SCTP_NODELAY的話,那麼程式會等收集到一定的量才送出資料,這個時候就會超過TTL的時間才送出去, 就會觸發SCTP_SEND_FAILED的事件啦, 以下的程式片斷
struct sockaddr_in sd;
int connfd;
int flag=1;
socklen_t sdlen = sizeof(sd);
sockfd = ::socket(AF_INET,SOCK_STREAM,IPPROTO_SCTP);
sctp_sa.sin_family = AF_INET;
sctp_sa.sin_port = htons(1234);
sctp_sa.sin_addr.s_addr = htons("127.0.0.1");

connfd = ::accept(sockfd,(struct sockaddr *) &sd, (socklen_t *)&sdlen);
setsockopt(connfd,IPPROTO_SCTP,SCTP_NODELAY,&flag,sizeof(flag));


2. 在sctp_sendmsg的ttl欄位要加入你要設定的時間, 若是0的話, 則表示沒有限制
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);
這裡另外提到,在one-to-one的情況下若to是NULL的話,則會送到目前這一個association的primary address,當to是NULL的話,tolen必需要設定為0

receiver端也有兩點要注意:
1. 要設定要監控sctp_send_failure_event
struct sctp_event_subscribe event;

event.sctp_send_failure_event = 1;
setsockopt(connfd,IPPROTO_SCTP,SCTP_EVENTS,&event,sizeof(event));


2. 由接收到的資料來判斷是否為sctp_send_failure_event事件, 以下是以one-to-one來當作例子

char inputBuffer[1024];
struct sockaddr_in peer,
int msg_flags;
struct sctp_sndrcvinfo sndrcvinfo;
socklen_t peerlen = sizeof(peer);
::sctp_recvmsg(connfd,inputBuffer,sizeof(inputBuffer),(struct sockaddr *) &peer,&peerlen,&sndrcvinfo,&msg_flag);
if (msg_flag & MSG_NOTIFICATION) // 判斷是否為事件, 若成立表示為事件
{
union sctp_notification *snp = (union sctp_notification *) inputBuffer;
switch (snp->sn_header.sn_type)
{
case SCTP_SEND_FAILED:
printf("SCTP_SEND_FAILED\n");
break;
default:
printf("unknow notification\n");
}
}


以上就可以判斷是否有Sendfail的事件啦

沒有留言: