SCTPを使ってみた//WIP

WIPです

本当に申し訳ないですが日本時間には間に合いそうにないです…… すみません(2016/12/6 AM 8:15)

はじめに

この記事はAizuAdventCalenderの記事である。 一つ前は misoton665 さん 一つ後は 0xShone さんです。 よろしくおねがいします。

モチベーション

SCTPと言うプロトコルを知り良さげだと感じたのとLinuxAPIがあると知って やってみたくなった

SCTPとは

Stream Control Transmission Protocol

と言うプロトコル

RFC4960で書かれている . RFC 4960 - Stream Control Transmission Protocol

簡単に説明するとTCPの様な信頼性は担保したいが制約が多すぎるので作られたのがSCTP。
公衆電話通信用に作られたが他の用途でも使うことができる。
TCP,UDPに比べあまりポピュラーではないがLTEトランスポート層でも採用されている。

TCPと比べて良い点

       Features                         TCP     SCTP
multi-streams support                    No     Yes
multi-homing support                     No     Yes
preservation of message boundaries       No     Yes
unordered reliable message delivery      No     Yes

マルチホーミングとは複数の経路を使ってアクセスすることができる機能

使い方

LinuxAPIでサポートされているので普通に利用できる https://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-10

環境

Ubuntu 16.0.4 gcc 5.4.1

下準備

sudo apt install libsctp-dev

簡単な実装(server)

#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/sctp.h>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <errno.h>

#define MAX_LISTEN 10
#define BUFSIZE 2048

int main(){
  int sockfd;
  struct sctp_sndrcvinfo sndrcvinfo;
  sockfd = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
  if(-1 == sockfd) {
    perror("error open socket");
    exit(EXIT_FAILURE);
  }

  struct sockaddr_in server, from;
  memset( &server, 0, sizeof(server));
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = htonl( INADDR_ANY );
  server.sin_port = htons(1204);

  if(-1 == bind(sockfd, (struct sockaddr *)&server, sizeof(server))) {
    perror("error bind");
    close(sockfd);
    exit(EXIT_FAILURE);
  }
  if(-1 == listen(sockfd,  MAX_LISTEN)) {
        exit(EXIT_FAILURE);
  }

  struct sctp_initmsg sctp_init;
  /*
  num_ostreams: number of outbound streams;
  max_instreams: max number of in-bound streams;
  max_attempts: max re-transmissions while establishing an association;
  max_init_timeo: time-out in milliseconds for establishing an association.
  */
  memset( &sctp_init, 0, sizeof(sctp_init));
  sctp_init.sinit_num_ostreams = 10;
  sctp_init.sinit_max_instreams = 10;
  sctp_init.sinit_max_attempts = 0;
  sctp_init.sinit_max_init_timeo = 0;

  int opt = 1;
  setsockopt(sockfd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_init, sizeof(sctp_init));

  if(listen(sockfd, 1) < 0) {
    perror("error listen for connection");
    exit(EXIT_FAILURE);
  }

  const char* message = "Hello world";
  char buffer[BUFSIZE];

  printf("start listen loop\n");
  int msg_flags;
  while(1) {
    int resv_size =  sctp_recvmsg(
      sockfd,
      buffer,
      strlen(buffer),
      (struct sockaddr *)&from, // sockaddr * from
      &len,            // socklen_t * fromlen
      &sndrcvinfo,             // struct sctp_sndrcvinfo
      &msg_flags               // int * msg_flags
    );
    if(-1 == resv_size) {
      perror("error receiving message");
      exit(EXIT_FAILURE);
    }

    buffer[resv_size] = 0;

    int ret = sctp_sendmsg(
      sockfd,
      (void *)buffer,
      (size_t)strlen(buffer),
      NULL, // struct sockaddr *to
      0,    // socklen_t tolen
      0,    // uint32_t ppid
      0,    // uint32_t flags
      0,    // uint16_t stream_no
      0,    // uint32_t timetolive
      0     // uint32_t context
    );

    if(-1 == ret) {
      perror("error sending message: ");
      exit(EXIT_FAILURE);
    }
  }

  return 0;
}

コンパイル

gcc sctp.c  -lsctp

進捗

error open socket: Socket type not supported

Ref

http://www.asahi-net.or.jp/~aa4t-nngk/ipttut/output/other/rfc3286.txt マルチホーミング

http://tech.queryhome.com/44164/what-is-the-main-advantage-of-using-sctp-over-tcp-ip