我的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
套接字(及相关资源)的设置/拆除是否有问题?
答案 0 :(得分:0)
这里的问题是侦听套接字进入TIME_WAIT并且在该状态下无法再次绑定。
即使CFSocket API没有返回任何错误,如果在使用POSIX套接字时发生相同的情况,则在尝试重新绑定套接字时会发生错误。
解决方案是在重新绑定套接字以便侦听之前简单地为套接字设置SO_REUSEADDR选项。