SCTP多宿主

我一直在开发这个简单的客户端-

使用C的服务器应用程序,其中客户端只是向服务器发送随机数据,而服务器只是侦听客户端发送的内容。我使用的协议是SCTP,我对如何对其实现多宿主功能感兴趣。

我一直在互联网上搜索有关SCTP和多宿主的信息,但找不到任何有关如何指示SCTP使用多个地址进行通信的示例。我仅设法找到了在尝试使用多宿主设置SCTP时应使用的命令,它应该非常简单。

我创建了一个客户端和一个服务器,它们都使用我的计算机的两个WLAN接口作为它们的连接点。两个适配器都连接到同一AP。服务器正在从这些接口监听来自客户端的数据,并且客户端通过它们发送数据。问题是,当我断开客户端向其发送数据的主WLAN适配器的连接时,传输应在回退到辅助连接时暂停。我用Wireshark跟踪了数据包,第一个INIT和INIT_ACK数据包报告客户端和服务器都使用WLAN适配器作为它们的通信链接。

当我重新连接主要的WLAN连接后,传输会在一段时间后继续进行,并导致向服务器发送大量数据包,这是不对的。数据包应该已经通过辅助连接传输了。在许多站点上,据说SCTP会自动在连接之间切换,但是在我看来,这没有发生。那么,你们有什么线索,即使在客户端和服务器知道彼此的地址(包括辅助地址)时,当主链路断开时,为什么传输也不回退到辅助连接?

关于服务器:

服务器创建一个SOCK_SEQPACKET类型的套接字,并绑定与INADDR_ANY找到的所有接口。getladdrs报告服务器已绑定到3个地址(包括127.0.0.1)。之后,服务器侦听套接字,并等待客户端发送数据。服务器通过sctp_recvmsg调用读取数据。

关于客户:

客户端还会创建一个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;

}

}

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

回答:

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

我使用sctp_paddrparams结构将心跳值调整为5000

ms。位于该结构中的flags变量必须处于SPP_HB_ENABLE模式下,因为否则,当尝试使用setsockopt()设置值时,SCTP会忽略心跳值。

这就是SCTP不能像我所希望的那样频繁发送心跳的原因。我没注意到flag变量的原因是我正在阅读的SCTP的过时参考指南,该指南指出该结构内不存在flags变量!较新的参考资料显示存在。这样心跳问题就解决了!

另一件事是将rto_max值修改为例如2000毫秒左右。降低该值将指示SCTP更快地更改路径。默认值为60 000

ms,该值过高(开始更改路径前1分钟)。可以使用sctp_rtoinfo结构调整rto_max值。

通过这两个修改,多宿主开始工作。哦,另一件事。当服务器处于SEQPACKET模式时,客户端必须处于STREAM模式。客户端使用普通的send()命令将数据发送到服务器,而服务器使用sctp_recvmsg()读取数据,其中addr

struct设置为NULL。

我希望这些信息可以帮助其他在SCTP多宿主方面苦苦挣扎的人。欢呼大家的意见,对我来说是很大的帮助!这是一些代码示例,因此如果您问我,这可能是网络中的第一个多宿主简单示例(除了多流示例外,没有找到其他示例)

服务器:

#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;

}

}

以上是 SCTP多宿主 的全部内容, 来源链接: utcz.com/qa/404453.html

回到顶部