CFSocketでIPv6マルチキャスト受信

CF系APIを使ってIPv6マルチキャストUDPパケットを受信するサンプルです。 エラー処理等かなりいい加減なのでご注意下さい。 説明文をそのうち書くかも知れませんが、今は面倒なのでコードを解読して下さい。

サンプルコード

このサンプルのポイントは、CFSocketCreate()をPF_INET6で作成しているところと、IPV6_JOIN_GROUPを利用してマルチキャストグループに参加しているところです。

IPv4のIP_ADD_MEMBERSHIPの受信インターフェース指定はネットワークインターフェースについているIPv4アドレスを利用して設定を行いますが、IPv6のIPV6_JOIN_GROUPはネットワークインターフェース番号を利用して設定を行うという違いがあるのでご注意下さい。 ネットワークインターフェース番号は、if_nametoindex()を利用して取得できます。


#import <Cocoa/Cocoa.h>
#import <netinet/in.h>
#import <sys/socket.h>
#import <arpa/inet.h>
#import <errno.h>

NSAutoreleasePool *pool = NULL;

void
myCallbackFunc(CFSocketRef socket, CFSocketCallBackType type,
    CFDataRef addr, const void *pData, void *pInfo) {

  if (type != kCFSocketDataCallBack) {
    NSLog(@"invalid type : %dn", type);
    return;
  }

  NSLog(@"len=%d, [%s]",
      CFDataGetLength((CFDataRef)pData),
      CFDataGetBytePtr((CFDataRef)pData));

  CFRunLoopStop(CFRunLoopGetCurrent());

  return;
}

void
showerror_and_release_pool()
{
  NSError *nserr;
  nserr = [NSError errorWithDomain:NSPOSIXErrorDomain
                     code:errno
                     userInfo:NULL];

  NSLog(@"%@", [nserr localizedDescription]);

  [pool release];
}

int
main()
{
  CFSocketRef sock;
  NSError *nserr;

  pool = [[NSAutoreleasePool alloc] init];

  sock = CFSocketCreate(kCFAllocatorDefault, PF_INET6, SOCK_DGRAM,
             IPPROTO_UDP,
             kCFSocketDataCallBack,
             (CFSocketCallBack)myCallbackFunc, NULL);  
  if (sock == NULL) {
    showerror_and_release_pool();
    return 1;
  }

  struct sockaddr_in6 myaddr;
  memset(&myaddr, 0, sizeof(myaddr));
  myaddr.sin6_len = sizeof(myaddr);
  myaddr.sin6_family = AF_INET6;
  myaddr.sin6_port = htons(10000);
  myaddr.sin6_addr = in6addr_any;

  CFDataRef myaddrData = CFDataCreate(NULL,
                             (unsigned char *)&myaddr, sizeof(myaddr));

  CFSocketError serr;

  serr = CFSocketSetAddress(sock, myaddrData);
  if (serr != kCFSocketSuccess) {
    showerror_and_release_pool();
    return 1;
  }

  struct ipv6_mreq mreq;
  memset(&mreq, 0, sizeof(mreq));
  mreq.ipv6mr_interface = if_nametoindex("en0"); /* change here */
  inet_pton(AF_INET6,
      "ff12::1111:cafe", &mreq.ipv6mr_multiaddr); /* change here */


  if (setsockopt(CFSocketGetNative(sock),
	IPPROTO_IPV6,
	IPV6_JOIN_GROUP,
	(char *)&mreq, sizeof(mreq)) != 0) {

    showerror_and_release_pool();
    return 1;
 }

  CFRunLoopSourceRef src;
  src = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sock, 0);
  CFRunLoopAddSource(CFRunLoopGetCurrent(), src, kCFRunLoopCommonModes);

  CFRunLoopRun();

  CFRelease(src);
  CFRelease(myaddrData);
  CFRelease(sock);

  [pool release];

  return 0;
}