SCTP Discussions

Text archives Help


[SCTP-Discussion] SCTP streams example


Chronological Thread 
  • From: Thomas Dreibholz <dreibh AT exp-math.uni-essen.de>
  • To: "Martin Nilsson" <martin.nilsson AT wp.pl>
  • Cc: discussion AT sctp.de
  • Subject: [SCTP-Discussion] SCTP streams example
  • Date: Wed Feb 5 16:17:01 2003
  • List-id: SCTP Discussions <discussion.sctp.de>
  • Organization: University of Essen, Institute for Experimental Mathematics

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am Mittwoch, 29. Januar 2003 14:57 schrieben Sie:
> Hello Thomas.
>
> I have quite a big problem with an application that I'm trying
> to write. (2 streams in one association: echo data back on one, and just
> ignore data sent on the second one). You sent me a general server plan
> (which I'm immensely grateful for), but I'm a schlecht programmer, and I
> have problems in order to make this whole thing work.
> I have worked on your scheme, and tried to write the missing
> parts myself, but unfortunately my limited programming knowledge doesn't
> allow me to complete the task. Could you perhaps be so kind and help me
> with the questions I put below?

Attached to this mail, you find an extended version of the
TCP_combined_server. On port 8, it has the desired service: All data on
streams with even stream number is echoed back (on streamID modulo the number
of outgoing streams) while all data on streams with uneven number is
discarded. This example shows you how to get and set the stream number and
also to obtain notifications via ext_recvmsg().

Start the program with very-verbose printing:
./TCP_combined_server -V

Now, start a test using the sctpterminal from the cppsocket_programs
directory:
./sctpterminal localhost:8 -in=16 -out=16
(Enter some text)
- -- The entered text is sent via stream #0 and echoed back on #0
#1:(Enter some text)
- -- The entered text is sent via stream #1 and discarded
#2/$aabbcc:(Enter some text)
- -- The entered text is sent via stream #2 and echoed back on #2. The PPID
used
is 0xaabbcc.


Best regards
- --
=======================================================================
Dipl.-Inform. Thomas Dreibholz

University of Essen, Room ES210
Inst. for Experimental Mathematics Ellernstraße 29
Computer Networking Technology Group D-45326 Essen/Germany
- -----------------------------------------------------------------------
E-Mail:
dreibh AT exp-math.uni-essen.de
Homepage: http://www.exp-math.uni-essen.de/~dreibh
=======================================================================
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQE+QSp+32BbsHYPLWURAsQTAKDMxhe/nesKvqJ8Libzsa1z3v8VNwCfTtdj
9iKfY6rxopQGSWJ4FtibnYI=
=mqpr
-----END PGP SIGNATURE-----
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <ext_socket.h>
#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>


#define ECHO_PORT         7
#define DISCARD_PORT      9
#define DAYTIME_PORT     13
#define CHARGEN_PORT     19
#define ECHODISCARD_PORT  8

#define SECONDS_TO_CLEANUP 5
#define BUFFER_SIZE 1024
#define MAXIMUM_NUMBER_OF_LOCAL_ADDRESSES    1 /* do not change this */

static int verbose  = 0;
static int vverbose = 0;
static int tooManyAddresses = 0;
static int unknownCommand = 0;
static struct sockaddr_in local_addr[MAXIMUM_NUMBER_OF_LOCAL_ADDRESSES];
static unsigned short noOfLocalAddresses = 0;

struct server_data {
    int fd;
    void * (* connection_handler)(void *);
};



void printUsage(void)
{
    printf("Usage:   combined_server [options]\n");
    printf("options:\n");
    printf("-s       source address\n");
    printf("-v       verbose mode\n");   
    printf("-V       very verbose mode\n");
}

void getArgs(int argc, char **argv)
{
    int c;
    extern char *optarg;
/*  extern int optind;*/

    while ((c = getopt(argc, argv, "hs:vV")) != -1)
    {
        switch (c) {
        case 'h':
            printUsage();
            exit(0);
        case 's':
            if (noOfLocalAddresses < MAXIMUM_NUMBER_OF_LOCAL_ADDRESSES) {
                    bzero(&local_addr[noOfLocalAddresses], sizeof(local_addr[0]));
#if !defined (LINUX)
                    local_addr[noOfLocalAddresses].sin_len    = sizeof(local_addr[0]);
#endif
                    local_addr[noOfLocalAddresses].sin_family = AF_INET;
                    if (inet_aton(optarg, &(local_addr[noOfLocalAddresses].sin_addr)) == 1) {
                        noOfLocalAddresses++;
                    }
            } else {
                tooManyAddresses = 1;
            }
            break;
        case 'v':
            verbose = 1;
            break;
        case 'V':
            verbose = 1;
            vverbose = 1;
            break;
        default:
            unknownCommand = 1;
            break;
       }
    }
}

void checkArgs(void)
{
    int abortProgram;
    int printUsageInfo;
    
    abortProgram = 0;
    printUsageInfo = 0;
    
    if (noOfLocalAddresses == 0) {
      bzero(&local_addr[noOfLocalAddresses], sizeof(local_addr[0]));    
#if !defined (LINUX)
      local_addr[noOfLocalAddresses].sin_len    = sizeof(local_addr[0]);
#endif
      local_addr[noOfLocalAddresses].sin_family = AF_INET;
      inet_aton("0.0.0.0", &(local_addr[noOfLocalAddresses].sin_addr));
      noOfLocalAddresses = 1;
    }
    if (tooManyAddresses == 1) {
        printf("Error:   Too many source addresses were specified. Taking only the %d first ones.\n",
               MAXIMUM_NUMBER_OF_LOCAL_ADDRESSES);
    }
    if (unknownCommand ==1) {
         printf("Error:   Unkown options in command.\n");
         printUsageInfo = 1;
    }
    
    if (printUsageInfo == 1)
        printUsage();
    if (abortProgram == 1)
        exit(-1);
}

static void*
handle_chargen(void *arg)
{
    int fd;
    int n, length;
    char buffer[BUFFER_SIZE];

    fd = *((int *) arg);
    free(arg);
    
    pthread_detach(pthread_self());
    
    length = 1 + (random() % 512);
    memset(buffer, 'A', length);
    buffer[length-1] = '\n';

    while ((n=ext_write(fd, buffer, length)) >  0){
        if (vverbose) {
            printf("%-6d: sending %u bytes.\n", fd, n);
            fflush(stdout);
        }
        if (n != ext_write(fd, buffer, n)) {
            printf("%-6d: Writing %u bytes failed.\n", fd, n);
        }
        length = 1 + (random() % 512);
        memset(buffer, 'A', length);
        buffer[length-1] = '\n';
    }
    ext_close(fd);
    if (verbose) {
        printf("%-6d: Connection closed.\n", fd);
        fflush(stdout);
    }
    return(NULL);
}

static void*
handle_discard(void *arg)
{
    int fd;
    int n;
    char buffer[BUFFER_SIZE];
    
    fd = *((int *) arg);
    free(arg);

    pthread_detach(pthread_self());
        
    while ((n=ext_read(fd, buffer, sizeof(buffer))) >  0){
        if (vverbose) {
            printf("%-6d: Discarding %u bytes.\n", fd, n);
            fflush(stdout);
        }
    }
    
    ext_close(fd);
    if (verbose) {
        printf("%-6d: Connection closed.\n", fd);
        fflush(stdout);
    }
    return(NULL);
}

static void*
handle_echo(void *arg)
{
    int fd;
    int n;
    char buffer[BUFFER_SIZE];

    fd = *((int *) arg);
    free(arg);

    pthread_detach(pthread_self());

    while ((n=ext_read(fd, buffer, sizeof(buffer))) >  0){
        if (vverbose) {
            printf("%-6d: Echoing %u bytes.\n", fd, n);
            fflush(stdout);
        }
        if (n != ext_write(fd, buffer, n)) {
            printf("%-6d: Writing %u bytes failed.\n", fd, n);
        }
    }
    ext_close(fd);
    if (verbose) {
        printf("%-6d: Connection closed.\n", fd);
        fflush(stdout);
    }
    return(NULL);
}

static void*
handle_echodiscard(void *arg)
{
    int                         fd;
    int                         n;
    char                        buffer[BUFFER_SIZE];
    char                        cbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
    unsigned short              streamID;
    unsigned int                protoID;
    unsigned int                inStreams;
    unsigned int                outStreams;
    struct sctp_event_subscribe events;
    union sctp_notification*    notification;
    struct sctp_assoc_change*   sac;
    struct sctp_sndrcvinfo*     info;
    struct iovec                iov;
    struct msghdr               msg;
    struct cmsghdr*             cmsg;

    inStreams  = 1;
    outStreams = 1;

    fd = *((int *) arg);
    free(arg);

    pthread_detach(pthread_self());

    bzero((char*)&events,sizeof(events));
    events.sctp_data_io_event     = 1;
    events.sctp_association_event = 1;
    if(ext_setsockopt(fd, IPPROTO_SCTP, SCTP_SET_EVENTS, &events, sizeof(events)) < 0)
       perror("setsockopt for SCTP_SET_EVENTS failed");

    for(;;) {
        iov.iov_base       = &buffer;
        iov.iov_len        = sizeof(buffer);
        msg.msg_name       = NULL;
        msg.msg_namelen    = 0;
        msg.msg_control    = &cbuf;
        msg.msg_controllen = sizeof(cbuf);
        msg.msg_iov        = &iov;
        msg.msg_iovlen     = 1;
        msg.msg_flags      = 0;

        n = ext_recvmsg(fd, &msg, 0);
        if(n <= 0) {
            break;
        }

        if(msg.msg_flags & MSG_NOTIFICATION) {
            notification = (union sctp_notification*)&buffer;
            if(notification->h.sn_type == SCTP_ASSOC_CHANGE) {
               sac = &notification->sn_assoc_change;
               inStreams  = sac->sac_inbound_streams;
               outStreams = sac->sac_outbound_streams;
               if (verbose) {
                  printf("%-6d: AssocChange event: in=%u out=%u.\n", fd, inStreams, outStreams);
               }
            }
        }
        else {
            streamID = 0;
            protoID  = 0;
            if(msg.msg_controllen > 0) {
               cmsg = CMSG_FIRSTHDR(&msg);
               if(cmsg != NULL) {
                  while(cmsg != NULL) {
                     if((cmsg->cmsg_level == IPPROTO_SCTP) &&
                        (cmsg->cmsg_type  == SCTP_SNDRCV)) {
                        info     = (struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
                        streamID = info->sinfo_stream;
                        protoID  = info->sinfo_ppid;
                     }
                     cmsg = CMSG_NXTHDR(&msg,cmsg);
                  }
               }
            }

            if (vverbose) {
                printf("%-6d: Received %u bytes, stream #%u, PPID $%08x.\n", fd, n, streamID, protoID);
                fflush(stdout);
            }
            if((streamID % 2) == 0) {
               iov.iov_base       = &buffer;
               iov.iov_len        = n;
               msg.msg_name       = NULL;
               msg.msg_namelen    = 0;
               msg.msg_control    = &cbuf;
               msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
               msg.msg_iov        = &iov;
               msg.msg_iovlen     = 1;
               msg.msg_flags      = 0;

               cmsg = (struct cmsghdr*)&cbuf;
               cmsg->cmsg_level = IPPROTO_SCTP;
               cmsg->cmsg_type  = SCTP_SNDRCV;
               cmsg->cmsg_len   = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));

               info                   = (struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
               info->sinfo_assoc_id   = 0;
               info->sinfo_flags      = 0;
               info->sinfo_stream     = streamID % outStreams;
               info->sinfo_ppid       = protoID;
               info->sinfo_timetolive = (unsigned int)-1;

               if (vverbose) {
                   printf("%-6d: Echoing %u bytes via stream #%u.\n", fd, n, info->sinfo_stream);
               }
               if (n != ext_sendmsg(fd, &msg, n)) {
                   printf("%-6d: Writing %u bytes failed.\n", fd, n);
               }
            }
            else {
               if (vverbose) {
                  printf("%-6d: Discarding %u bytes.\n", fd, n);
               }
            }
        }
    }
    ext_close(fd);
    if (verbose) {
        printf("%-6d: Connection closed.\n", fd);
        fflush(stdout);
    }
    return(NULL);
}

static void*
handle_daytime(void *arg)
{
    int fd;
    int timeAsStringLen;
    char *timeAsString;
    time_t now;

    fd = *((int *) arg);
    free(arg);

    pthread_detach(pthread_self());
    
    time(&now);
    timeAsString = ctime(&now);
    timeAsStringLen = strlen(timeAsString);
    
    if (timeAsStringLen != ext_write(fd, timeAsString, timeAsStringLen)) {
            printf("%-6d: Writing the daytime failed.\n", fd);
    }
    if (vverbose) {
        printf("%-6d: Daytime sent back: %s", fd, timeAsString);
        fflush(stdout);
    }

    ext_close(fd);
    if (verbose) {
        printf("%-6d: Connection closed.\n", fd);
        fflush(stdout);
    }
    return(NULL);
}

static void*
start_server(void *arg)
{
    pthread_t tid;
    int *fdp;
    struct sockaddr_in remote_addr;
    int remote_addr_len;
    struct server_data data;

    data = *((struct server_data *) arg);
    free(arg);

    bzero(&remote_addr, sizeof(remote_addr));
    remote_addr_len = sizeof(remote_addr);
    for(;;){
        fdp = malloc(sizeof(int));
        if((*fdp=ext_accept(data.fd, (struct sockaddr *) &remote_addr, &remote_addr_len)) < 0)
            perror("accept call failed");
        if (verbose) {
            printf("%-6d: Connection accepted from %s:%u.\n", *fdp, inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
            fflush(stdout);
        }
        pthread_create(&tid, NULL, data.connection_handler, fdp);
    }
    return(NULL);
}

static pthread_t
init_server(struct sockaddr_in local_addr, int port, void *(connection_handler ()))
{
    pthread_t                   tid;
    int                         fd;
    struct sctp_initmsg         initmsg;
    struct sctp_event_subscribe events;
    struct server_data          *server_data_p;

    if ((fd = ext_socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0)
        perror("socket call failed");

    initmsg.sinit_num_ostreams   = 16;
    initmsg.sinit_max_instreams  = 16;
    initmsg.sinit_max_attempts   = 5;
    initmsg.sinit_max_init_timeo = 10000;
    if(ext_setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, (char*)&initmsg, sizeof(initmsg)) < 0)
       perror("setsockopt for SCTP_INITMSG failed");

    bzero((char*)&events,sizeof(events));
    events.sctp_data_io_event     = 1;
    events.sctp_association_event = 1;
    if(ext_setsockopt(fd, IPPROTO_SCTP, SCTP_SET_EVENTS, &events, sizeof(events)) < 0)
       perror("setsockopt for SCTP_SET_EVENTS failed");

    local_addr.sin_port= htons(port);
    if(ext_bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
        perror("bind call failed");

    if(ext_listen(fd, 0) < 0)
        perror("listen call failed");

    server_data_p = malloc(sizeof(struct server_data));
    server_data_p->fd = fd;
    server_data_p->connection_handler = connection_handler;
    if (verbose) {
        printf("Starting server on port %u.\n", port);
        fflush(stdout);
    }
    pthread_create(&tid, NULL, &start_server, (void *)server_data_p);
    return(tid);
}

int main (int argc, char * argv[])
{
    pthread_t echo_tid, discard_tid, daytime_tid, chargen_tid, echodiscard_tid;

    getArgs(argc, argv);
    checkArgs();
      
    echo_tid        = init_server(local_addr[0], ECHO_PORT,    &handle_echo);
    discard_tid     = init_server(local_addr[0], DISCARD_PORT, &handle_discard);
    daytime_tid     = init_server(local_addr[0], DAYTIME_PORT, &handle_daytime);
    chargen_tid     = init_server(local_addr[0], CHARGEN_PORT,     &handle_chargen);
    echodiscard_tid = init_server(local_addr[0], ECHODISCARD_PORT, &handle_echodiscard);

    pthread_join(echo_tid, NULL);
    pthread_join(discard_tid, NULL);
    pthread_join(daytime_tid, NULL);
    pthread_join(chargen_tid, NULL);
    pthread_join(echodiscard_tid, NULL);

    printf("Give the SCTP thread time to clean up ...");
    fflush(stdout);
    sleep(SECONDS_TO_CLEANUP);
    printf(" the time is over now!\n");
    return 0;
}


  • [SCTP-Discussion] SCTP streams example, Thomas Dreibholz, 02/05/2003

Archive powered by MHonArc 2.6.19+.

Top of page