私は現在、Objective-C で特定のポートにバインドしてリッスンできる単純な TCP サーバー クラスを作成しています。特に-(void) start
、CFSockets を作成し、指定されたポートにバインドし、実行ループに追加する関数があります。次のようになります。
- (void) start {
//This function is called after the initialiser
if([self isStarted]) //if already started, return without doing anything
return;
//create a socket
CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL};
socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)&SocketAcceptCallback, &socketCtxt);
NSAssert(socket, @"Error: Cannot create a socket...");
int yes = 1;
setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
//bind it to the desired port
struct sockaddr_in serverAddress;
socklen_t nameLen = 0;
nameLen = sizeof(serverAddress);
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_len = nameLen;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(self.port);
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
NSData* address4 = [NSData dataWithBytes:&serverAddress length:nameLen];
if(kCFSocketSuccess != CFSocketSetAddress(socket, (CFDataRef)address4)) {
if (socket) CFRelease(socket);
socket = NULL;
NSAssert(NO, @"Unable to bind the created socket to the desired port");
}
//add to the current run loop
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
assert(rls!=0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
//done!
}
また、コールバック関数を次のSocketAcceptCallback
ように定義します。
static void SocketAcceptCallback(CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
//this method is called after a connection is accepted
//here we bind the received connection to a pair of
//NSStreams
if(type == kCFSocketAcceptCallBack){
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
struct sockaddr_in peerAddress;
socklen_t peerLen = sizeof(peerAddress);
NSString * peer = nil;
if (getpeername(nativeSocketHandle, (struct sockaddr *)&peerAddress, (socklen_t *)&peerLen) == 0) {
peer = [NSString stringWithUTF8String:inet_ntoa(peerAddress.sin_addr)];
} else {
peer = @"Generic Peer";
}
NSLog(@"%@ has connected", peer);
//Find corresponding SocketServer object -- it is passed to the callback in the INFO pointer
SocketServer *obj = (__bridge SocketServer*)info;
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocket(NULL, nativeSocketHandle, &readStream, &writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
//bridge to NSStreams
obj.inputStream = (NSInputStream *)readStream;
obj.outputStream = (NSOutputStream *)writeStream;
//set delegates
[obj.inputStream setDelegate:obj];
[obj.outputStream setDelegate:obj];
//schedule the runloops
[obj.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[obj.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//open connections
[obj.inputStream open];
[obj.outputStream open];
} else {
// on any failure, need to destroy the CFSocketNativeHandle
// since we are not going to use it any more
close(nativeSocketHandle);
}
/*
if (readStream) CFRelease(readStream);
if (writeStream) CFRelease(writeStream); */
}
}
コールバックでは、クラスのメンバー フィールドとして保持し、読み取りと書き込みに使用する NSStream のペアへの接続をブリッジします。
問題:このサーバーを実行して、telnet 経由で接続しようとしました。接続しますが、前述のコールバックが呼び出されていません(診断 NSLog を追加して確認しました)。ソケットを実行ループに正しく追加していないと思われますが、自分で間違いを見つけることはできません。それが何であるかについてのアイデアはありますか?