CFSocket で UDP を待ち受ける : Objective-C プログラミング

PROGRAM


CFSocket で UDP を待ち受ける

CFSocket を使用して UDP ポートの待ち受けを行う場合には、次のような流れになります。

ここでは、クラス "MyClass" に、UDP による待ち受けを実装しているものとして、話を進めて行くことにします。

 

MyClass.h 内での定義

// 必要なヘッダーは次のような感じです。

#import <Foundation/Foundation.h>

#import <netinet/in.h>

#import <sys/socket.h>

#import <arpa/inet.h>

 

 

@interface MyClass : NSObject

 

// パケットを受信したときに呼び出されるコールバック関数を定義します。(C 形式)

static void MyClassSocketReceivedCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void* pData, void* pInfo);

 

// パケットを受信したときに呼び出されるコールバック関数を定義します。(Objective-C 形式)

- (void)socketReceivedCallback:(CFSocketRef)socket type:(CFSocketCallBackType)type address:(CFDataRef)address data:(NSData*)pData;

 

MyClass.mm での実装部分

// パケットを受信したときに呼び出されるコールバック関数を定義します。

static void MyClassSocketReceivedCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void* pData, void* pInfo)

{

// 即座に Objective-C 形式のコールバックを呼ぶようにしています。

[(MyClass*)pInfo socketRecievedCallback:socket type:type address:address data:(NSData*)pData];

}

 

// Objective-C 形式のコールバックメソッドでパケット受信の処理を行います。

- (void)socketReceivedCallback:(CFSocketRef)socket type:(CFSocketCallBackType)type address:(CFDataRef)address data:(NSData*)pData

{

}

 

// UDP によるソケットの待ち受けを行います。

- (void)listening

{

CFSocketRef socket;

CFSocketContext context;

 

// ソケットに設定するコンテキストを準備します。

// version は必ず 0 を指定し、ここでは info に self を設定しています。

context.version = 0;

context.info = (void*)self;

context.retain = NULL;

context.release = NULL;

context.copyDescription = NULL;

 

// UDP ソケットを生成します。データを受信した時に呼び出すコールバック関数もここで指定します。

socket = CFSocketCreate(kCFAllocaterDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP, kCFSocketDataCallBack, (CFSocketCallBack)MyClassSocketReceivedCallback &context);

 

if (socket != NULL)

{

// UDP ソケットが生成できたら、続いてアドレスへのバインドを行います。

CFDataRef address;

struct sockaddr_in addr;

 

// アドレスを設定する構造体 sockaddr_in を初期化しています。

memset(&addr, 0, sizeof(struct sockaddr_in));

 

// 待ち受けに使用するアドレスとポートを設定します。

// 任意のアドレスで待ち受けたい場合は INADDR_ANY を sin_addr.s_addr に設定します。

addr.sin_family = AF_INET;

addr.sin_port = htons(10000);

addr sin_addr.s_addr = inet_addr("192.168.1.1");

 

// 先ほどの sockaddr_in を使用して、ソケットに割り当てるアドレスデータを生成します。

address = CFDataCreateWithBytesNoCopy(NULL, (UInt8*)&addr, sizeof(struct sockaddr_in), kCFAllocatorNull);

 

// ソケットをバインドします。

if (CFSocketSetAddress(socket, address) == kCFSocketSuccess)

{

// バインドに成功したら、CFRunLoop を使ってパケットが到着するのを待ちます。

CFRunLoopSourceRef source;

 

// ソケットから CFRunLoop に登録するための入力ソースを取得します。

source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);

 

// ソケットの入力ソースをカレントランループに追加します。

CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);

 

// 登録し終わった、手元の入力ソースを解放します。

CFRelease(source);

}

else

{

// バインドに失敗した場合は、ソケットの後始末を行います。

CFSocketInvalidate(socket);

 

// 他、必要なエラー処理を行います。

 

}

 

// 登録し終えて、手元に残ったアドレスデータとソケットを解放します。

CFRelease(address);

CFRelease(socket);

}

else

{

// ソケットの生成に失敗した場合の処理です。

}

}

このようにすることで、上記の例では "192.168.1.1:10000/UDP" での待ち受けを開始し、にデータが到着した時に、コールバックメソッド "socketRecievedCallback:type:address:data:" が呼び出されるようになります。

勝手がわかるまでは長くて難しそうなコードですけど、順を追って読んでみると、それほど難しくない流れになっています。

このように CFRunLoop を使用すると、データが到着したかどうかとか、そういったデータの待ちを行う処理を全て Core Foundation に任せることができるので、プログラミングで必要なこととしては、待ち受けるアドレスやポートとプロトコルを設定するといった程度になります。

[ もどる ]