おおよそ次の手順に従う iOS アプリケーションがあります。
- リスニング ソケットを開きます。
- 単一のクライアント接続を受け入れます。
- クライアントとのデータ交換を実行します。
- 「resign active」イベントを受け取ると、クライアントとサーバーのソケットに関連付けられたすべてのリソースを閉じて解放します (つまり、すべての実行ループ ソース、読み取り/書き込みストリーム、およびソケット自体を無効にして解放します)。
- アクティブを再開すると、リスニング ソケットを元に戻して通信を継続します (クライアントは、手順 4 で iOS アプリがアクティブを辞退した後、再接続できるようになるまで再接続を試み続けます)。
クライアントとサーバーの間で接続が行われるたびに、ステップ 5 の後に、リッスンのためにサーバー ソケットを再度開くことができずにアプリケーションが再開されることがわかります。つまり、手順 5 ですべてが解放されても、アプリケーションは再バインドしてソケット アドレスをリッスンすることができません。さらに悪いことに、リッスン ソケットを再度セットアップしようとしても、CFSocket API 呼び出しでエラーが検出されません。
一方、iOS アプリケーションがアクティブを辞退し、以前に接続を受信せずに再び再開した場合、クライアントは、アプリケーションが辞任して再び再開するまで、一度だけ接続できます。この場合、上記と同じ動作が観察されます。 .
この問題を説明する最小限のアプリケーションの例は、次のリポジトリにあります。
https://github.com/dpereira/cfsocket_reopen_bug
最も関連性の高いソースは次のとおりです。
#import "AppDelegate.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
static void _handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
NSLog(@"Connected ...");
close(*(CFSocketNativeHandle*)data);
NSLog(@"Closed ...");
}
@interface AppDelegate ()
@end
@implementation AppDelegate {
CFRunLoopSourceRef _source;
CFSocketRef _serverSocket;
CFRunLoopRef _socketRunLoop;
}
- (void)applicationWillResignActive:(UIApplication *)application {
CFRunLoopRemoveSource(self->_socketRunLoop, self->_source, kCFRunLoopCommonModes);
CFRunLoopSourceInvalidate(self->_source);
CFRelease(self->_source);
self->_source = nil;
CFSocketInvalidate(self->_serverSocket);
CFRelease(self->_serverSocket);
self->_serverSocket = nil;
CFRunLoopStop(self->_socketRunLoop);
NSLog(@"RELASED SUCCESSFULLY!");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
CFSocketContext ctx = {0, (__bridge void*)self, NULL, NULL, NULL};
self->_serverSocket = CFSocketCreate(kCFAllocatorDefault,
PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack, _handleConnect, &ctx);
NSLog(@"Socket created %u", self->_serverSocket != NULL);
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(30000);
sin.sin_addr.s_addr= INADDR_ANY;
CFDataRef sincfd = CFDataCreate(kCFAllocatorDefault,
(UInt8 *)&sin,
sizeof(sin));
CFSocketSetAddress(self->_serverSocket, sincfd);
CFRelease(sincfd);
self->_source = CFSocketCreateRunLoopSource(kCFAllocatorDefault,
self->_serverSocket,
0);
NSLog(@"Created source %u", self->_source != NULL);
self->_socketRunLoop = CFRunLoopGetCurrent();
CFRunLoopAddSource(self->_socketRunLoop,
self->_source,
kCFRunLoopCommonModes);
NSLog(@"Registered into run loop");
NSLog(@"Socket is %s", CFSocketIsValid(self->_serverSocket) ? "valid" : "invalid");
NSLog(@"Source is %s", CFRunLoopSourceIsValid(self->_source) ? "valid" : "invalid");
}
@end
本格的なアプリはhttps://github.com/dpereira/confluxにあります。
ソケット (および関連リソース) のセットアップ/ティアダウンに何か問題がありますか?