SCTP多宿主

我一直在用C开发这个简单的客户端 – 服务器应用程序,客户端只是将随机数据发送到服务器,而服务器只是监听客户端发送的内容。 我使用的协议是SCTP,我对如何实现多宿主function感兴趣。

我一直在通过互联网search有关SCTP和多宿主的信息,而且还没有find任何有关如何指示SCTP使用多个地址进行通信的例子。 我只设法find在尝试使用多宿主设置SCTP时应该使用的命令,它应该是非常简单的。

我创build了一个客户端和一个服务器,它们都使用我的电脑两个WLAN接口作为连接点。 两个适配器都连接到相同的AP。 服务器正在从这些接口监听客户端的数据,客户端通过它们发送数据。 问题是,当我断开主要的无线局域网适配器客户端正在发送数据,传输刚刚停止,当它应该回退到次要连接。 我用Wireshark跟踪数据包,第一个INIT和INIT_ACK数据包报告客户端和服务器都使用WLAN适配器作为它们的通信链路。

当我重新连接主要的无线局域网连接时,传输会在一段时间后继续,并且将大量的数据包破坏到不正确的服务器。 数据包应该通过辅助连接传输。 在许多网站上,据说SCTP在连接之间自动切换,但在我的情况下,这没有发生。 那么你们有没有什么线索,为什么当主链路断开时,即使客户端和服务器知道其他地址(包括第二地址),传输也不会回退到第二连接?

关于服务器:

服务器创build一个SOCK_SEQPACKETtypes的套接字,并将所有与INADDR_ANYfind的接口绑定。 getladdrs报告服务器被绑定到3个地址(包括127.0.0.1)。 之后,服务器侦听套接字并等待客户端发送数据。 服务器使用sctp_recvmsg调用读取数据。

关于客户:

客户端也创build一个SEQPACKET套接字并连接到由命令行参数指定的IP地址。 在这种情况下,getladdrs也返回3个地址,如服务器的情况。 之后,客户端开始向服务器发送数据,延迟一秒,直到用户用Ctrl-C中断发送。

这里有一些源代码:

服务器:

#define BUFFER_SIZE (1 << 16) #define PORT 10000 int sock, ret, flags; int i; int addr_count = 0; char buffer[BUFFER_SIZE]; socklen_t from_len; struct sockaddr_in addr; struct sockaddr_in *laddr[10]; struct sockaddr_in *paddrs[10]; struct sctp_sndrcvinfo sinfo; struct sctp_event_subscribe event; struct sctp_prim prim_addr; struct sctp_paddrparams heartbeat; struct sigaction sig_handler; void handle_signal(int signum); int main(void) { if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) perror("socket"); memset(&addr, 0, sizeof(struct sockaddr_in)); memset((void*)&event, 1, sizeof(struct sctp_event_subscribe)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(PORT); from_len = (socklen_t)sizeof(struct sockaddr_in); sig_handler.sa_handler = handle_signal; sig_handler.sa_flags = 0; if(sigaction(SIGINT, &sig_handler, NULL) == -1) perror("sigaction"); if(setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0) perror("setsockopt"); if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))< 0) perror("setsockopt"); if(bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0) perror("bind"); if(listen(sock, 2) < 0) perror("listen"); addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddr); printf("Addresses binded: %d\n", addr_count); for(i = 0; i < addr_count; i++) printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddr)[i].sin_addr), (*laddr)[i].sin_port); sctp_freeladdrs((struct sockaddr*)*laddr); while(1) { flags = 0; ret = sctp_recvmsg(sock, buffer, BUFFER_SIZE, (struct sockaddr*)&addr, &from_len, NULL, &flags); if(flags & MSG_NOTIFICATION) printf("Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); printf("%d bytes received from %s:%u\n", ret, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } if(close(sock) < 0) perror("close"); } void handle_signal(int signum) { switch(signum) { case SIGINT: if(close(sock) != 0) perror("close"); exit(0); break; default: exit(0); break; } } 

客户:

 #define PORT 10000 #define MSG_SIZE 1000 #define NUMBER_OF_MESSAGES 1000 #define PPID 1234 int sock; struct sockaddr_in *paddrs[10]; struct sockaddr_in *laddrs[10]; void handle_signal(int signum); int main(int argc, char **argv) { int i; int counter = 1; int ret; int addr_count; char address[16]; char buffer[MSG_SIZE]; sctp_assoc_t id; struct sockaddr_in addr; struct sctp_status status; struct sctp_initmsg initmsg; struct sctp_event_subscribe events; struct sigaction sig_handler; memset((void*)&buffer, 'j', MSG_SIZE); memset((void*)&initmsg, 0, sizeof(initmsg)); memset((void*)&addr, 0, sizeof(struct sockaddr_in)); memset((void*)&events, 1, sizeof(struct sctp_event_subscribe)); if(argc != 2 || (inet_addr(argv[1]) == -1)) { puts("Usage: client [IP ADDRESS in form xxx.xxx.xxx.xxx] "); return 0; } strncpy(address, argv[1], 15); address[15] = 0; addr.sin_family = AF_INET; inet_aton(address, &(addr.sin_addr)); addr.sin_port = htons(PORT); initmsg.sinit_num_ostreams = 2; initmsg.sinit_max_instreams = 2; initmsg.sinit_max_attempts = 5; sig_handler.sa_handler = handle_signal; sig_handler.sa_flags = 0; if(sigaction(SIGINT, &sig_handler, NULL) == -1) perror("sigaction"); if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) perror("socket"); if((setsockopt(sock, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg))) != 0) perror("setsockopt"); if((setsockopt(sock, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events))) != 0) perror("setsockopt"); if(sendto(sock, buffer, MSG_SIZE, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == -1) perror("sendto"); addr_count = sctp_getpaddrs(sock, 0, (struct sockaddr**)paddrs); printf("\nPeer addresses: %d\n", addr_count); for(i = 0; i < addr_count; i++) printf("Address %d: %s:%d\n", i +1, inet_ntoa((*paddrs)[i].sin_addr), (*paddrs)[i].sin_port); sctp_freepaddrs((struct sockaddr*)*paddrs); addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddrs); printf("\nLocal addresses: %d\n", addr_count); for(i = 0; i < addr_count; i++) printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddrs)[i].sin_addr), (*laddrs)[i].sin_port); sctp_freeladdrs((struct sockaddr*)*laddrs); i = sizeof(status); if((ret = getsockopt(sock, SOL_SCTP, SCTP_STATUS, &status, (socklen_t *)&i)) != 0) perror("getsockopt"); printf("\nSCTP Status:\n--------\n"); 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--------\n\n", status.sstat_outstrms); for(i = 0; i < NUMBER_OF_MESSAGES; i++) { counter++; printf("Sending data chunk #%d...", counter); if((ret = sendto(sock, buffer, MSG_SIZE, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr))) == -1) perror("sendto"); printf("Sent %d bytes to peer\n",ret); sleep(1); } if(close(sock) != 0) perror("close"); } void handle_signal(int signum) { switch(signum) { case SIGINT: if(close(sock) != 0) perror("close"); exit(0); break; default: exit(0); break; } } 

那么你们有什么线索我做错了吗?

Solutions Collecting From Web of "SCTP多宿主"

好的,我终于解决了多宿主问题。 这是我做的。

我用sctp_paddrparams结构将心跳值调整到了5000毫秒。 位于结构体中的flags变量必须在SPP_HB_ENABLE模式下,因为否则当试图使用setsockopt()设置值时,SCTP忽略心跳值。

这就是为什么SCTP没有按照我想要的频率发送心跳信号的原因。 原因,为什么我没有注意到标志变量,是我正在阅读的SCTP废弃的参考指南,它指出,在结构中不存在标志变量! 较新的参考显示有。 所以心跳问题解决了!

另一件事是修改rto_max值,例如,2000毫秒左右。 降低该值将告诉SCTP更早更改路径。 默认值是60 000 ms,这个值太高(在开始改变路径之前1分钟)。 rto_max的值可以用sctp_rtoinfo结构来调整。

有了这两个修改,多宿主就开始工作了。 哦,另一件事。 当服务器处于SEQPACKET模式时,客户端必须处于STREAM模式。 客户端使用正常的send()命令向服务器发送数据,服务器使用sctp_recvmsg()读取数据,其中addr结构被设置为NULL。

我希望这些信息能够帮助其他人解决SCTP的多重归属问题。 欢呼你们的意见,这些对我来说是一个很大的帮助! 这里是一些代码示例,所以这可能是第一个多宿主简单的例子,如果你问我(没有找到任何比multistreaming例子)

服务器:

 #include <sys/types.h> #include <sys/socket.h> #include <signal.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <arpa/inet.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <sys/ioctl.h> #include <net/if.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE (1 << 16) #define PORT 10000 int sock, ret, flags; int i, reuse = 1; int addr_count = 0; char buffer[BUFFER_SIZE]; socklen_t from_len; struct sockaddr_in addr; struct sockaddr_in *laddr[10]; struct sockaddr_in *paddrs[10]; struct sctp_sndrcvinfo sinfo; struct sctp_event_subscribe event; struct sctp_prim prim_addr; struct sctp_paddrparams heartbeat; struct sigaction sig_handler; struct sctp_rtoinfo rtoinfo; void handle_signal(int signum); int main(void) { if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) perror("socket"); memset(&addr, 0, sizeof(struct sockaddr_in)); memset(&event, 1, sizeof(struct sctp_event_subscribe)); memset(&heartbeat, 0, sizeof(struct sctp_paddrparams)); memset(&rtoinfo, 0, sizeof(struct sctp_rtoinfo)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(PORT); from_len = (socklen_t)sizeof(struct sockaddr_in); sig_handler.sa_handler = handle_signal; sig_handler.sa_flags = 0; heartbeat.spp_flags = SPP_HB_ENABLE; heartbeat.spp_hbinterval = 5000; heartbeat.spp_pathmaxrxt = 1; rtoinfo.srto_max = 2000; /*Set Heartbeats*/ if(setsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS , &heartbeat, sizeof(heartbeat)) != 0) perror("setsockopt"); /*Set rto_max*/ if(setsockopt(sock, SOL_SCTP, SCTP_RTOINFO , &rtoinfo, sizeof(rtoinfo)) != 0) perror("setsockopt"); /*Set Signal Handler*/ if(sigaction(SIGINT, &sig_handler, NULL) == -1) perror("sigaction"); /*Set Events */ if(setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0) perror("setsockopt"); /*Set the Reuse of Address*/ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))< 0) perror("setsockopt"); /*Bind the Addresses*/ if(bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0) perror("bind"); if(listen(sock, 2) < 0) perror("listen"); /*Get Heartbeat Value*/ i = (sizeof heartbeat); getsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeat, (socklen_t*)&i); printf("Heartbeat interval %d\n", heartbeat.spp_hbinterval); /*Print Locally Binded Addresses*/ addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddr); printf("Addresses binded: %d\n", addr_count); for(i = 0; i < addr_count; i++) printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddr)[i].sin_addr), (*laddr)[i].sin_port); sctp_freeladdrs((struct sockaddr*)*laddr); while(1) { flags = 0; ret = sctp_recvmsg(sock, buffer, BUFFER_SIZE, NULL, 0, NULL, &flags); if(flags & MSG_NOTIFICATION) printf("Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); printf("%d bytes received from %s:%u\n", ret, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } if(close(sock) < 0) perror("close"); } void handle_signal(int signum) { switch(signum) { case SIGINT: if(close(sock) != 0) perror("close"); exit(0); break; default: exit(0); break; } } 

客户:

 #include <sys/types.h> #include <sys/socket.h> #include <signal.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <arpa/inet.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #define PORT 11000 #define MSG_SIZE 1000 #define NUMBER_OF_MESSAGES 1000 int sock; struct sockaddr_in *paddrs[5]; struct sockaddr_in *laddrs[5]; void handle_signal(int signum); int main(int argc, char **argv) { int i; int counter = 0; int asconf = 1; int ret; int addr_count; char address[16]; char buffer[MSG_SIZE]; sctp_assoc_t id; struct sockaddr_in addr; struct sctp_status status; struct sctp_initmsg initmsg; struct sctp_event_subscribe events; struct sigaction sig_handler; struct sctp_paddrparams heartbeat; struct sctp_rtoinfo rtoinfo; memset(&buffer, 'j', MSG_SIZE); memset(&initmsg, 0, sizeof(struct sctp_initmsg)); memset(&addr, 0, sizeof(struct sockaddr_in)); memset(&events, 1, sizeof(struct sctp_event_subscribe)); memset(&status, 0, sizeof(struct sctp_status)); memset(&heartbeat, 0, sizeof(struct sctp_paddrparams)); memset(&rtoinfo, 0, sizeof(struct sctp_rtoinfo)) if(argc != 2 || (inet_addr(argv[1]) == -1)) { puts("Usage: client [IP ADDRESS in form xxx.xxx.xxx.xxx] "); return 0; } strncpy(address, argv[1], 15); address[15] = 0; addr.sin_family = AF_INET; inet_aton(address, &(addr.sin_addr)); addr.sin_port = htons(PORT); initmsg.sinit_num_ostreams = 2; initmsg.sinit_max_instreams = 2; initmsg.sinit_max_attempts = 1; heartbeat.spp_flags = SPP_HB_ENABLE; heartbeat.spp_hbinterval = 5000; heartbeat.spp_pathmaxrxt = 1; rtoinfo.srto_max = 2000; sig_handler.sa_handler = handle_signal; sig_handler.sa_flags = 0; /*Handle SIGINT in handle_signal Function*/ if(sigaction(SIGINT, &sig_handler, NULL) == -1) perror("sigaction"); /*Create the Socket*/ if((ret = (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP))) < 0) perror("socket"); /*Configure Heartbeats*/ if((ret = setsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS , &heartbeat, sizeof(heartbeat))) != 0) perror("setsockopt"); /*Set rto_max*/ if((ret = setsockopt(sock, SOL_SCTP, SCTP_RTOINFO , &rtoinfo, sizeof(rtoinfo))) != 0) perror("setsockopt"); /*Set SCTP Init Message*/ if((ret = setsockopt(sock, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg))) != 0) perror("setsockopt"); /*Enable SCTP Events*/ if((ret = setsockopt(sock, SOL_SCTP, SCTP_EVENTS, (void *)&events, sizeof(events))) != 0) perror("setsockopt"); /*Get And Print Heartbeat Interval*/ i = (sizeof heartbeat); getsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeat, (socklen_t*)&i); printf("Heartbeat interval %d\n", heartbeat.spp_hbinterval); /*Connect to Host*/ if(((ret = connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)))) < 0) { perror("connect"); close(sock); exit(0); } /*Get Peer Addresses*/ addr_count = sctp_getpaddrs(sock, 0, (struct sockaddr**)paddrs); printf("\nPeer addresses: %d\n", addr_count); /*Print Out Addresses*/ for(i = 0; i < addr_count; i++) printf("Address %d: %s:%d\n", i +1, inet_ntoa((*paddrs)[i].sin_addr), (*paddrs)[i].sin_port); sctp_freepaddrs((struct sockaddr*)*paddrs); /*Start to Send Data*/ for(i = 0; i < NUMBER_OF_MESSAGES; i++) { counter++; printf("Sending data chunk #%d...", counter); if((ret = send(sock, buffer, MSG_SIZE, 0)) == -1) perror("write"); printf("Sent %d bytes to peer\n",ret); sleep(1); } if(close(sock) != 0) perror("close"); } void handle_signal(int signum) { switch(signum) { case SIGINT: if(close(sock) != 0) perror("close"); exit(0); break; default: exit(0); break; } } 

你的客户使用sendto()调用打开一个协调。 没关系。 但之后,你不应该再使用sendto()。 因为sendto()将有利地强制SCTP使用给定IP的接口。 所以,使用write()或send()代替sendto():

 counter++; printf("Sending data chunk #%d...", counter); if((ret = write(sock, buffer, MSG_SIZE) == -1) perror("write"); 

我从来没有尝试SCTP,但根据这个网站它支持两种模式。

一对一的风格和一对多的风格。

据我所知,多重国籍的作品是一对一的风格。

为了打开一个一个样式的套接字,你需要一个如下的调用:

 connSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP ); 

请注意, SOCK_STREAM与您的SOCK_SEQPACKET不同

当你这样做

 sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP) 

它似乎打开一对多风格的插座,我不明白,如果它支持多宿主与否。

所以试试SOCK_STREAM参数的代码。

这里也是一个使用SOCK_STREAM的例子。