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

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

送信側サンプルコード

送信側サンプルは、IP_MULTICAST_IFを利用してマルチキャスト送出インターンフェースを指定しています。 マルチキャスト送出インターフェースはネットワークインターフェースに付いているIPv4アドレスで指定します。


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

NSAutoreleasePool *pool = NULL;

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

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

  [pool release];
}

int
main()
{
  CFSocketRef sock;
  CFSocketError err;

  pool  = [[NSAutoreleasePool alloc] init];

  sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM,
             IPPROTO_UDP,
             kCFSocketNoCallBack,
             NULL, NULL);  
  if (sock == NULL) {
    showerror_and_release_pool();
    return 1;
  }

  struct sockaddr_in dest;
  memset(&dest, 0, sizeof(dest));
  dest.sin_len = sizeof(dest);
  dest.sin_family = AF_INET;
  dest.sin_port = htons(10000);
  inet_aton("239.192.1.2", &dest.sin_addr); /* change here */

  in_addr_t ifaddr;
  ifaddr = inet_addr("10.1.2.3"); /* change here */
  if (setsockopt(CFSocketGetNative(sock),
          IPPROTO_IP,
          IP_MULTICAST_IF,
          (char *)&ifaddr, sizeof(ifaddr)) != 0) {
    showerror_and_release_pool();
    return 1;
  }

  CFDataRef destData = CFDataCreate(NULL, (unsigned char *)&dest, sizeof(dest));

  CFDataRef msgData = CFDataCreate(NULL, (unsigned char *)"HOGE", 4);

  err = CFSocketSendData(sock, destData, msgData, 4);
  if (err != kCFSocketSuccess) {
    showerror_and_release_pool();
    return 1;
  }

  [pool release];

  return 0;
}

受信側サンプルコード

IP_ADD_MEMBERSHIPでマルチキャストグループにJOINしているのがポイントです。


#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 : %d", 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_INET, SOCK_DGRAM,
             IPPROTO_UDP,
             kCFSocketDataCallBack,
             (CFSocketCallBack)myCallbackFunc, NULL);  
  if (sock == NULL) {
    showerror_and_release_pool();
    return 1;
  }

  struct sockaddr_in myaddr;
  memset(&myaddr, 0, sizeof(myaddr));
  myaddr.sin_len = sizeof(myaddr);
  myaddr.sin_family = AF_INET;
  myaddr.sin_port = htons(10000);
  myaddr.sin_addr.s_addr = INADDR_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 ip_mreq mreq;
  memset(&mreq, 0, sizeof(mreq));
  mreq.imr_interface.s_addr = INADDR_ANY;
  mreq.imr_multiaddr.s_addr = inet_addr("239.192.1.2"); /* change here */

  if (setsockopt(CFSocketGetNative(sock),
	IPPROTO_IP,
	IP_ADD_MEMBERSHIP,
	(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;
}