SagittariusA
SagittariusA

Reputation: 5427

SCTP Multistreaming: infinite loop

I have a simple client-server application on SCTP! The client connects to server opening 3 streams and the server sends a file per stream. The problem is that I don't know how to control the 3 streams, to know when sctp_rcvmsg() from a stream i 0 it would mean that the file transmission is ended for that stream...but it seems that sctp_recvmsg() never stops. here's my code. CLIENT

#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 <time.h>

#define BUFFERSIZE 1024

int main(int argc, char** argv) {

int i, sockCliSCTP, flags, res;

/* Server netwrok informations */
struct sockaddr_in servAddr;

/* To get which stream it has received data from */
struct sctp_sndrcvinfo sndrcvinfo;

/* Init message to setup number of streams */
struct sctp_initmsg initmsg;

/* Catching events */
struct sctp_event_subscribe events;

/* Buffer to receive files */
char buffer[BUFFERSIZE];

/* Remove previous recently used files */
remove("first.txt");
remove("second.txt");
remove("third.txt");

char ipServ[32] = "127.0.0.1";
short int servPort = 29008;

/* BEGIN SCTP PART */
/* Creating client socket for SCTP protocol */
sockCliSCTP = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );

/* Specify that a maximum of 3 streams will be available per socket */
memset( &initmsg, 0, sizeof(initmsg) );
initmsg.sinit_num_ostreams = 3; /* output streams */
initmsg.sinit_max_instreams = 3; /* input streams */
initmsg.sinit_max_attempts = 2;
setsockopt(sockCliSCTP, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg) );

/* Initializing server network data structs */
bzero( (void *)&servAddr, sizeof(servAddr) );
servAddr.sin_family = AF_INET;
inet_pton(AF_INET, ipServ, &servAddr.sin_addr);
servAddr.sin_port = htons(29008);
int sizeServ = sizeof(servAddr);

/* Connect to server */
res = connect(sockCliSCTP, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (res < 0) {
    printf("Connection to server refused!\n");
    exit(1);
}

memset( (void *)&events, 0, sizeof(events) );
events.sctp_data_io_event = 1;
res = setsockopt(sockCliSCTP, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events));
if (res < 0) {
    printf("setsockopt failed!\n");
    exit(1);
}

/* The clients simply waits and receives for three files from the server.
 * The size of the files is increased each time this client is launched. */

FILE *oneF, *twoF, *threeF;
oneF = fopen("first.txt", "a"); /* Stream 0 */
twoF = fopen("second.txt", "a"); /* Stream 1 */
threeF = fopen("third.txt", "a"); /* Stream 2 */

/* To measure time */
time_t timeStart;
time_t timeEnd;
time_t timeRes = 0;

time(&timeStart);

int count0 = 0, count1 = 0, count2 = 0;

int checkRead[3];
for(i = 0; i<3; i++) {
    checkRead[i] = 1;
}


/* Receiving in parallel the files from 3 streams */
while(checkRead[0] || checkRead[1] || checkRead[2]) {

    printf("%d %d %d\n", checkRead[0], checkRead[1], checkRead[2]);

    res = sctp_recvmsg(sockCliSCTP, (void*)buffer, sizeof(buffer), (struct sockaddr*)&servAddr, (socklen_t *)&sizeServ, &sndrcvinfo, &flags);

    if (res == 0) {
        printf("%d stream is zero\n", sndrcvinfo.sinfo_stream);
        checkRead[sndrcvinfo.sinfo_stream] = 0;
        continue;
    }


    /* Check from which stream the data came in */
    switch(sndrcvinfo.sinfo_stream) {

    /* Write on file oneF --> first.txt */
    case 0:
        count0++;
        printf("Message received from stream 0\n");
        //printf("%s\n\n", buffer);
        fprintf(oneF, "%s", buffer);
        break;



    /* Write on file twoF --> second.txt */
    case 1:
        count1++;
        printf("Message received from stream 1\n");
        //printf("%s\n\n", buffer);
        fprintf(twoF, "%s", buffer);
        break;




    /* Write on file threeF --> third.txt */
    case 2:
        count2++;
        printf("Message received from stream 2\n");
        //printf("%s\n\n", buffer);
        fprintf(threeF, "%s", buffer);
        break;


    }

    memset(buffer, 0, sizeof(buffer));
    sleep(1);
}

close(sockCliSCTP);

time(&timeEnd);
timeRes = timeEnd - timeStart;
printf("Time elapsed is: %d seconds\n", (int)timeRes);

printf("%d messages on stream 0,\n %d messages on stream 1,\n %d messages on stream 2\n", count0, count1, count2);


}

AND THE SERVER:

#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 <time.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUFFERSIZE 1024

int main(int argc, char** argv) {

int sockCli, sockServ, one, two, three, i, res;

struct sockaddr_in client, server;

/* data struct to declarate streams */
struct sctp_initmsg initmsg;

/* buffer to read from file */
char buffer[BUFFERSIZE];

/* socket server listening */
sockServ = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);

bzero( (void *)&client, sizeof(client));
bzero( (void *)&server, sizeof(server));


/* Preparing sever data struct and bind() */
bzero( (void *)&server, sizeof(server) );
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl( INADDR_ANY );
server.sin_port = htons(29008);

bind(sockServ, (struct sockaddr *)&server, sizeof(server));

/* Maximum of 3 streams will be available per socket */
memset( &initmsg, 0, sizeof(initmsg) );
initmsg.sinit_num_ostreams = 3;
initmsg.sinit_max_instreams = 3;
initmsg.sinit_max_attempts = 2;

res = setsockopt(sockServ, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
if (res < 0) {
    printf("setsockopt() failed!\n");
    exit(1);
}


/* Preparing the three files to be sent */
one = open("files/first.txt", O_RDONLY);
if (one < 0) {
    printf("Error on opening first file!\n");
    exit(1);
}

two = open("files/second.txt", O_RDONLY);
if (two < 0) {
    printf("Error on opening second file!\n");
    exit(1);
}

three = open("files/third.txt", O_RDONLY);
if (three < 0) {
    printf("Error on opening third files!\n");
    exit(1);
}


int checkFiles[3];
for(i=0; i<3; i++) {
    checkFiles[i] = 1;
}


res = listen(sockServ, 5);
if (res < 0) {
    printf("listen() failed!\n");
    exit(1);
}



while(1) {

    ssize_t readRes;
    int len = sizeof(client);
    sockCli = accept(sockServ, (struct sockaddr*)&client, &len);

    if (sockCli < 0) {
        printf("Error on accept()!\n");
        exit(1);
    }

    printf("Associated to client!\n");

    while(1) {

        memset(buffer, 0, sizeof(buffer));
        if ((readRes = read(one, (void*)buffer, sizeof(buffer))) > 0) {
            sctp_sendmsg(sockCli, (void*)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, 0 /* stream number */, 0, 0);
        }


        memset(buffer, 0, sizeof(buffer));
        if ((readRes = read(two, (void*)buffer, sizeof(buffer))) > 0) {
            sctp_sendmsg(sockCli, (void*)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, 1 /* stream number */, 0, 0);
        }


        memset(buffer, 0, sizeof(buffer));
        if ((readRes = read(three, (void*)buffer, sizeof(buffer))) > 0) {
            sctp_sendmsg(sockCli, (void*)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, 2 /* stream number */, 0, 0);
        }

        else {break;}

    }

    close(sockCli);
    close(one);
    close(two);
    close(three);




}



}

Where am I making a mistake? :(

Upvotes: 1

Views: 1044

Answers (1)

chulochumo
chulochumo

Reputation: 21

sctp_recvmsg doesn't receive a message from a single stream. It simply returns ANY message received and then the application can figure out which stream the message came from.

After all the data has been received by your client when this code executes:

res = sctp_recvmsg(sockCliSCTP, (void*)buffer, sizeof(buffer), (struct sockaddr*)&servAddr, (socklen_t *)&sizeServ, &sndrcvinfo, &flags);

if (res == 0) {
    printf("%d stream is zero\n", sndrcvinfo.sinfo_stream);
    checkRead[sndrcvinfo.sinfo_stream] = 0;
    continue;
}

res becomes 0 since no message is received and the sndrcvinfo structure does not get changed. So sndrcvinfo.sinfo_stream will remain equal to whatever stream the last message came from and you will get stuck in a loop since you won't change the checkRead[] values.

There's some other errors that will make the server/client behave strangely.

For example you can't run the client twice in a row without a a segmentation fault since the server closes the file descriptors and won't send any data the second time. Because of this you will segfault when you do:

 checkRead[sndrcvinfo.sinfo_stream] = 0;

since sndrcvinfo will be a null pointer.

Upvotes: 2

Related Questions