编程接口指南

SCTP 用法代码示例

本节详细介绍 SCTP 套接字的两种用法。


示例 7–17 SCTP 回显客户机

/*

 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.

 * Use is subject to license terms.

 */



/* To enable socket features used for SCTP socket. */

#define _XPG4_2

#define __EXTENSIONS__



#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#include <netinet/sctp.h>

#include <netdb.h>



#define BUFLEN 1024



static void

usage(char *a0)

{

 fprintf(stderr, "Usage: %s <addr>\n", a0);

}



/*

 * Read from the network.

 */

static void

readit(void *vfdp)

{

 int   fd;

 ssize_t   n;

 char   buf[BUFLEN];

 struct msghdr  msg[1];

 struct iovec  iov[1];

 struct cmsghdr  *cmsg;

 struct sctp_sndrcvinfo *sri;

 char   cbuf[sizeof (*cmsg) + sizeof (*sri)];

 union sctp_notification *snp;



 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);



 fd = *(int *)vfdp;



 /* Initialize the message header for receiving */

 memset(msg, 0, sizeof (*msg));

 msg->msg_control = cbuf;

 msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

 msg->msg_flags = 0;

 cmsg = (struct cmsghdr *)cbuf;

 sri = (struct sctp_sndrcvinfo *)(cmsg + 1);

 iov->iov_base = buf;

 iov->iov_len = BUFLEN;

 msg->msg_iov = iov;

 msg->msg_iovlen = 1;



 while ((n = recvmsg(fd, msg, 0)) > 0) {

  /* Intercept notifications here */

  if (msg->msg_flags & MSG_NOTIFICATION) {

   snp = (union sctp_notification *)buf;

   printf("[ Receive notification type %u ]\n",

       snp->sn_type);

   continue;

  }

  msg->msg_control = cbuf;

  msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

  printf("[ Receive echo (%u bytes): stream = %hu, ssn = %hu, "

      "flags = %hx, ppid = %u ]\n", n,

      sri->sinfo_stream, sri->sinfo_ssn, sri->sinfo_flags,

      sri->sinfo_ppid);

 }



 if (n < 0) {

     perror("recv");

     exit(1);

 }



 close(fd);

 exit(0);

}



#define MAX_STREAM 64



/*

 *

 */

static void

echo(struct sockaddr_in *addr)

{

 int     fd;

 uchar_t     buf[BUFLEN];

 ssize_t    n;

 int    perr;

 pthread_t   tid;

 struct cmsghdr   *cmsg;

 struct sctp_sndrcvinfo  *sri;

 char    cbuf[sizeof (*cmsg) + sizeof (*sri)];

 struct msghdr   msg[1];

 struct iovec   iov[1];

 int    ret;

 struct sctp_initmsg   initmsg;

 struct sctp_event_subscribe events;



 /* Create a one-one SCTP socket */

 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {

  perror("socket");

  exit(1);

 }



 /*

  * We are interested in association change events and we want

  * to get sctp_sndrcvinfo in each receive.

  */

 events.sctp_association_event = 1; 

 events.sctp_data_io_event = 1; 

 ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &events,

     sizeof (events));

 if (ret < 0) {

  perror("setsockopt SCTP_EVENTS");

  exit(1);

 }



 /*

  * Set the SCTP stream parameters to tell the other side when

  * setting up the association.

  */

 memset(&initmsg, 0, sizeof(struct sctp_initmsg));

 initmsg.sinit_num_ostreams = MAX_STREAM;

 initmsg.sinit_max_instreams = MAX_STREAM;

 initmsg.sinit_max_attempts = MAX_STREAM;

 ret = setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,

     sizeof(struct sctp_initmsg));

 if (ret < 0) {

  perror("setsockopt SCTP_INITMSG");

  exit(1);

 }



 if (connect(fd, (struct sockaddr *)addr, sizeof (*addr)) == -1) {

  perror("connect");

  exit(1);

 }



 /* Initialize the message header structure for sending. */

 memset(msg, 0, sizeof (*msg));

 iov->iov_base = buf;

 msg->msg_iov = iov;

 msg->msg_iovlen = 1;

 msg->msg_control = cbuf;

 msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

 msg->msg_flags |= MSG_XPG4_2;



 memset(cbuf, 0, sizeof (*cmsg) + sizeof (*sri));

 cmsg = (struct cmsghdr *)cbuf;

 sri = (struct sctp_sndrcvinfo *)(cmsg + 1);



 cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sri);

 cmsg->cmsg_level = IPPROTO_SCTP;

 cmsg->cmsg_type  = SCTP_SNDRCV;



 sri->sinfo_ppid   = 1;

 /* Start sending to stream 0. */

 sri->sinfo_stream = 0;



 /* Create a thread to receive network traffic. */

 perr = pthread_create(&tid, NULL, (void *(*)(void *))readit, &fd);



 if (perr != 0) {

  fprintf(stderr, "pthread_create: %d\n", perr);

  exit(1);

 }



 /* Read from stdin and then send to the echo server. */

 while ((n = read(fileno(stdin), buf, BUFLEN)) > 0) {

  iov->iov_len = n;

  if (sendmsg(fd, msg, 0) < 0) {

   perror("sendmsg");

   exit(1);

  }

  /* Send the next message to a different stream. */

  sri->sinfo_stream = (sri->sinfo_stream + 1) % MAX_STREAM;

 }



 pthread_cancel(tid);

 close(fd);

}



int

main(int argc, char **argv)

{

 struct sockaddr_in addr[1];

 struct hostent *hp;

 int error;



 if (argc < 2) {

     usage(*argv);

     exit(1);

 }



 /* Find the host to connect to. */ 

 hp = getipnodebyname(argv[1], AF_INET, AI_DEFAULT, &error);

 if (hp == NULL) {

  fprintf(stderr, "host not found\n");

  exit(1);

 }



 addr->sin_family = AF_INET;

 addr->sin_addr.s_addr = *(ipaddr_t *)hp->h_addr_list[0];

 addr->sin_port = htons(5000);



 echo(addr);



 return (0);

}


示例 7–18 SCTP 回显服务器

/*

 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.

 * Use is subject to license terms.

 */



/* To enable socket features used for SCTP socket. */

#define _XPG4_2

#define __EXTENSIONS__



#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <stdlib.h>

#include <unistd.h>

#include <netinet/sctp.h>



#define BUFLEN 1024



/*

 * Given an event notification, print out what it is.

 */

static void

handle_event(void *buf)

{

 struct sctp_assoc_change *sac;

 struct sctp_send_failed  *ssf;

 struct sctp_paddr_change *spc;

 struct sctp_remote_error *sre;

 union sctp_notification  *snp;

 char    addrbuf[INET6_ADDRSTRLEN];

 const char   *ap;

 struct sockaddr_in  *sin;

 struct sockaddr_in6  *sin6;



 snp = buf;



 switch (snp->sn_header.sn_type) {

 case SCTP_ASSOC_CHANGE:

  sac = &snp->sn_assoc_change;

  printf("^^^ assoc_change: state=%hu, error=%hu, instr=%hu "

      "outstr=%hu\n", sac->sac_state, sac->sac_error,

      sac->sac_inbound_streams, sac->sac_outbound_streams);

  break;

 case SCTP_SEND_FAILED:

  ssf = &snp->sn_send_failed;

  printf("^^^ sendfailed: len=%hu err=%d\n", ssf->ssf_length,

      ssf->ssf_error);

  break;

 case SCTP_PEER_ADDR_CHANGE:

  spc = &snp->sn_paddr_change;

  if (spc->spc_aaddr.ss_family == AF_INET) {

   sin = (struct sockaddr_in *)&spc->spc_aaddr;

   ap = inet_ntop(AF_INET, &sin->sin_addr, addrbuf,

       INET6_ADDRSTRLEN);

  } else {

   sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;

   ap = inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf,

       INET6_ADDRSTRLEN);

  }

  printf("^^^ intf_change: %s state=%d, error=%d\n", ap,

      spc->spc_state, spc->spc_error);

  break;

 case SCTP_REMOTE_ERROR:

  sre = &snp->sn_remote_error;

  printf("^^^ remote_error: err=%hu len=%hu\n",

      ntohs(sre->sre_error), ntohs(sre->sre_length));

  break;

 case SCTP_SHUTDOWN_EVENT:

  printf("^^^ shutdown event\n");

  break;

 default:

  printf("unknown type: %hu\n", snp->sn_header.sn_type);

  break;

 }

}



/*

 * Receive a message from the network.

 */

static void *

getmsg(int fd, struct msghdr *msg, void *buf, size_t *buflen,

    ssize_t *nrp, size_t cmsglen)

{

 ssize_t  nr = 0;

 struct iovec iov[1];



 *nrp = 0;

 iov->iov_base = buf;

 msg->msg_iov = iov;

 msg->msg_iovlen = 1;



 /* Loop until a whole message is received. */

 for (;;) {

  msg->msg_flags = MSG_XPG4_2;

  msg->msg_iov->iov_len = *buflen;

  msg->msg_controllen = cmsglen;



  nr += recvmsg(fd, msg, 0);

  if (nr <= 0) {

   /* EOF or error */

   *nrp = nr;

   return (NULL);

  }



  /* Whole message is received, return it. */

  if (msg->msg_flags & MSG_EOR) {

   *nrp = nr;

   return (buf);

  }



  /* Maybe we need a bigger buffer, do realloc(). */

  if (*buflen == nr) {

   buf = realloc(buf, *buflen * 2);

   if (buf == 0) {

    fprintf(stderr, "out of memory\n");

    exit(1);

   }

   *buflen *= 2;

  }



  /* Set the next read offset */

  iov->iov_base = (char *)buf + nr;

  iov->iov_len = *buflen - nr;



 }

}



/*

 * The echo server.

 */

static void

echo(int fd)

{

 ssize_t   nr;

 struct sctp_sndrcvinfo *sri;

 struct msghdr  msg[1];

 struct cmsghdr  *cmsg;

 char   cbuf[sizeof (*cmsg) + sizeof (*sri)];

 char   *buf;

 size_t   buflen;

 struct iovec  iov[1];

 size_t   cmsglen = sizeof (*cmsg) + sizeof (*sri);



 /* Allocate the initial data buffer */

 buflen = BUFLEN;

 if ((buf = malloc(BUFLEN)) == NULL) {

  fprintf(stderr, "out of memory\n");

  exit(1);

 }



 /* Set up the msghdr structure for receiving */

 memset(msg, 0, sizeof (*msg));

 msg->msg_control = cbuf;

 msg->msg_controllen = cmsglen;

 msg->msg_flags = 0;

 cmsg = (struct cmsghdr *)cbuf;

 sri = (struct sctp_sndrcvinfo *)(cmsg + 1);



 /* Wait for something to echo */

 while ((buf = getmsg(fd, msg, buf, &buflen, &nr, cmsglen)) != NULL) {



  /* Intercept notifications here */

  if (msg->msg_flags & MSG_NOTIFICATION) {

   handle_event(buf);

   continue;

  }



  iov->iov_base = buf;

  msg->msg_iov = iov;

  msg->msg_iovlen = 1;

  iov->iov_len = nr;

  msg->msg_control = cbuf;

  msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);



  printf("got %u bytes on stream %hu:\n", nr,

      sri->sinfo_stream);

  write(0, buf, nr);



  /* Echo it back */

  msg->msg_flags = MSG_XPG4_2;

  if (sendmsg(fd, msg, 0) < 0) {

   perror("sendmsg");

   exit(1);

  }

 }



 if (nr < 0) {

  perror("recvmsg");

 }

 close(fd);

}



int

main(void)

{

 int    lfd;

 int    cfd;

 int    onoff = 1;

 struct sockaddr_in  sin[1];

 struct sctp_event_subscribe events;

 struct sctp_initmsg  initmsg;



 if ((lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {

  perror("socket");

  exit(1);

 }



 sin->sin_family = AF_INET;

 sin->sin_port = htons(5000);

 sin->sin_addr.s_addr = INADDR_ANY;

 if (bind(lfd, (struct sockaddr *)sin, sizeof (*sin)) == -1) {

  perror("bind");

  exit(1);

 }



 if (listen(lfd, 1) == -1) {

  perror("listen");

  exit(1);

 }



 (void) memset(&initmsg, 0, sizeof(struct sctp_initmsg));

 initmsg.sinit_num_ostreams = 64;

 initmsg.sinit_max_instreams = 64;

 initmsg.sinit_max_attempts = 64;

 if (setsockopt(lfd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,

     sizeof(struct sctp_initmsg)) < 0) { 

  perror("SCTP_INITMSG");

  exit (1);

 }



 /* Events to be notified for */

 (void) memset(&events, 0, sizeof (events));

 events.sctp_data_io_event = 1;

 events.sctp_association_event = 1;

 events.sctp_send_failure_event = 1;

 events.sctp_address_event = 1;

 events.sctp_peer_error_event = 1;

 events.sctp_shutdown_event = 1;



 /* Wait for new associations */

 for (;;) {

  if ((cfd = accept(lfd, NULL, 0)) == -1) {

   perror("accept");

   exit(1);

  }



  /* Enable ancillary data */

  if (setsockopt(cfd, IPPROTO_SCTP, SCTP_EVENTS, &events,

      sizeof (events)) < 0) {

   perror("setsockopt SCTP_EVENTS");

   exit(1);

  }

  /* Echo back any and all data */

  echo(cfd);

 }

}