J'ai développé une application client-serveur simple en C où le client envoie des données aléatoires au serveur et le serveur écoute ce que le client envoie. Le protocole que j'utilise est SCTP et je m'intéresse à la façon d'y implémenter la fonction multihoming.
J'ai fait des recherches sur Internet à propos de SCTP et du multihoming et je n'ai pas pu trouver d'exemples sur la façon de demander à SCTP d'utiliser plusieurs adresses pour communiquer. J'ai seulement réussi à trouver les commandes à utiliser pour configurer SCTP avec le multihoming et cela devrait être assez simple.
J'ai créé un client et un serveur qui utilisent tous deux les deux interfaces WLAN de mon ordinateur comme points de connexion. Les deux adaptateurs sont connectés au même AP. Le serveur écoute les données du client à partir de ces interfaces et le client envoie des données par leur intermédiaire. Le problème est que lorsque je déconnecte l'adaptateur WLAN primaire vers lequel le client envoie des données, la transmission s'arrête alors qu'elle devrait revenir à la connexion secondaire. J'ai suivi les paquets avec Wireshark et les premiers paquets INIT et INIT_ACK indiquent que le client et le serveur utilisent les adaptateurs WLAN comme liens de communication.
Lorsque je reconnecte la connexion WLAN primaire, la transmission continue après un petit moment et envoie une énorme charge de paquets au serveur, ce qui n'est pas normal. Les paquets auraient dû être transmis par la connexion secondaire. Sur de nombreux sites, il est dit que SCTP passe automatiquement d'une connexion à l'autre, mais dans mon cas, ce n'est pas le cas. Avez-vous une idée de la raison pour laquelle la transmission ne bascule pas vers la connexion secondaire lorsque la liaison primaire est interrompue, même si le client et le serveur connaissent leurs adresses respectives, y compris l'adresse secondaire ?
A propos du serveur :
Le serveur crée un socket de type SOCK_SEQPACKET et lie toutes les interfaces trouvées avec INADDR_ANY. getladdrs rapporte que le serveur est lié à 3 adresses (dont 127.0.0.1). Après cela, le serveur écoute le socket et attend que le client envoie des données. Le serveur lit les données avec l'appel sctp_recvmsg.
À propos du client :
Le client crée aussi un socket SEQPACKET et se connecte à une adresse IP spécifiée par un argument de ligne de commande. getladdrs dans ce cas retourne aussi 3 adresses comme dans le cas des serveurs. Ensuite, le client commence à envoyer des données au serveur avec un délai d'une seconde jusqu'à ce que l'utilisateur interrompe l'envoi avec Ctrl-C.
Voici un peu de code source :
Serveur :
#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;
}
}
Et le client :
#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;
}
}
Vous avez une idée de ce que je fais de mal ?