I created NSStreams to transfer something using the below code:
dispatch_async(dispatch_get_main_queue(), ^{
// open input
[self.inputStream setDelegate:controller];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
// open output
[self.outputStream setDelegate:controller];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream open];
});
At some point, I want to close the streams using the below code:
// Shut down input
[self.inputStream close];
self.inputStream = nil;
// Shut down output
[self.outputStream close];
self.outputStream = nil;
NSLog(#"input streams status:%i", [self.inputStream streamStatus]);
NSLog(#"output streams status:%i", [self.outputStream streamStatus]);
NSLog(#"input streams status:%#", [self.inputStream streamError]);
NSLog(#"output streams status:%#", [self.outputStream streamError]);
All input/output streams are strong properties.
Stream status return zero, but my app hangs. When I put device in debug mode, the monitor shows that my device CPU start running in 98+% after close method called. It seems like it's waiting for something to finish, but I still don't know what that is.
Does anyone know what may cause this issues?
Try removing the streams from the run loop.
// Shut down input
[self.inputStream close];
[self.inputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
self.inputStream = nil;
// Shut down output
[self.outputStream close];
[self.outputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
self.outputStream = nil;
More information can be found here.
Related
I'm trying to configure and iOS device to connect to a macOS device over a WiFi network using Bonjour and NSStreams. I'd like to secure the connection via SSL. I started from Apple's Cocoa Echo example code here. It works beautifully without SSL, but when I try to configure a secure connection, I'm getting CFNetwork SSLHandshake failed (-9800) on both the client and server side. Here's my configuration.
macOS (server)
// establish connection
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
SQConnection * connection = [[SQConnection alloc] initWithInputStream:(__bridge NSInputStream *)readStream outputStream:(__bridge NSOutputStream *)writeStream];
[self.connections addObject:connection];
[connection open];
[(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(SQConnectionDidCloseNotification:) name:SQConnectionDidCloseNotification object:connection];
NSLog(#"Added connection.");
}
// open streams (in SQConnection.open)
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
// SSL
[self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
[self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
#(FALSE), kCFStreamSSLValidatesCertificateChain,
kCFNull, kCFStreamSSLPeerName,
nil];
CFReadStreamSetProperty((CFReadStreamRef)self.inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)self.outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
iOS (client)
NSInputStream * istream;
NSOutputStream * ostream;
if ([netService qNetworkAdditions_getInputStream:&istream outputStream:&ostream]) {
NSLog(#"OPEN STREAMS");
self.inputStream = istream;
self.outputStream = ostream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
[self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
}
My best guess is that I haven't actually configured a certificate to be used for SSL negotiation. This is something I was hoping macOS would take care of automatically, but Apple's docs are woefully incomplete on this. The best I've found is a couple paragraphs here on "Securing and Configuring the Connection."
I'm building an application that interfaces with external accessories (thus with the ExternalAccessory framework).
Part of this interfacing is waiting for events to occur on the in/out streams to the external device.
For that, I use a thread, and block it in the NSRunLoop to make sure it keeps waiting for events.
- (void)eventThreadSelector
{
// Setting up the environment
self.session = <got the EASession>;
self.inputStream = [self.session inputStream];
self.inputStream.delegate = self;
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
self.outputStream = [self.session outputStream];
self.outputStream.delegate = self;
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream open];
// Waiting for events coming from the NSStreams
while (![[NSThread currentThread] isCancelled]) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
}
// When the device is unplugged,
[self.inputStream close];
[self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.inputStream.delegate = nil;
self.inputStream = nil;
[self.outputStream close];
[self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.outputStream.delegate = nil;
self.outputStream = nil;
self.session = nil;
}
- (void)accessoryDidDisconnect:(EAAccessory *)accessory
{
DDLogVerbose(#"EID: Got accessoryDidDisconnect notification on main thread? %i", [[NSThread currentThread] isMainThread]);
[self.eventThread cancel];
self.eventThread = nil;
}
So far, all of this works great :) But there's one thing I can't wrap my head around.
When I unplug the external device, the processor briefly jumps to 100% and above. I've done a little bit of investigation, and this seems to be linked to the NSRunLoop going nuts. That CPU peak has a negative impact on the UI :(
How can I avoid this processor usage peak?
I am newbie in ios development, I am doing an application using tcp socket, and followed several tutorials and everything related to the connection works great. but the ui freezes until my authentication is completed. for this use "dispatch_queue" follows
-(BOOL)Connect {
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^ {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)IP,PORT, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream =(__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[[NSRunLoop currentRunLoop] run];
});
return true;
}
up here, the connection works fine and does not lock. now my problem is that when I write something on the outputstream not send anything.
from another class by pressing a button I need to send something to the server, the function is called but does not send nothing, because it is in another thread I think.
-(void)Send:(NSMutableData *)_output{
[outputStream write:[_output bytes] maxLength:[_output length]];
}
How I can send something in outputstream if it is in another thread?
excuse my spelling, I do not speak much English
reading some forums, I found that NSThreads can pass data between them with:
performSelector:onThread:withObject:waitUntilDone:
but i don't know how I can create an instance of thread that is already running. Someone can help me?
for NS Thread just add all the above code in a function and call it like this:
[self performSelectorOnMainThread:#selector(TcpSocketFunction) withObject:nil waitUntilDone:NO];
Where TcpSocketFunction is the function name with a defination like.
- (void) TcpSocketFunction {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)IP,PORT, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream =(__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[[NSRunLoop currentRunLoop] run];
}
Currently I'm developing a VoIP application. In wifi connection keeps me perfectamente. The problem is when I use 3g. After about 6 minutes in the background, and VoIP socket doesn't receive any call. But apparently it's connected. The problem isn't SIP server as this allows me to renew the registration after 10 minutes. Any idea?
Thanks.
I add code:
AppDelegate *app = [[AppDelegate alloc] init];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
int misocket = zObtenerSocket();
CFStreamCreatePairWithSocket(NULL, misocket, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType,kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
inputStream = ( NSInputStream *)readStream;
outputStream = ( NSOutputStream *)writeStream;
[inputStream setDelegate:app];
[inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
[outputStream setDelegate:app];
[outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
cierra_extra();
zbCerrar();
[inputStream close];
[outputStream close];
registrarse_bg();
}];
registrarse_bg() reopens the socket and create the again
Does anyone know of a simple example that creates (network-based) NSStreams on a separate thread?
What I am actually trying to do is unschedule (from the main thread) and reschedule (to a helper/network thread) an open NSInputStream and NSOutputStream that I am receiving from a third party framework (see Can an open, but inactive, NSStream that is scheduled on the main thread be moved to a different thread?). Nobody has answered that question thus far, so I am trying to do this myself to see if it could be made to work.
To test what is possible, I am trying to modify the code here (which includes a iOS client and a very short, python-based server [awesome!]):
http://www.raywenderlich.com/3932/how-to-create-a-socket-based-iphone-app-and-server
so that after the NSInputStream and NSOutputStream is created and opened I attempt to move it onto a helper thread.
The challenge that I am experiencing is that the helper thread does not seem to be responding to delegate messages from the streams or any messages that I am sending via:
performSelector:onThread:withObject:waitUntilDone:modes:. I suspect that I am doing something wrong with setting up the helper thread's NSRunLoop (see networkThreadMain below).
So, [ChatClientViewController viewDidLoad] now looks like:
- (void)viewDidLoad {
[super viewDidLoad];
[self initNetworkCommunication];
[self decoupleStreamsFromMainThread];
[self spoolUpNetworkThread];
inputNameField.text = #"cesare";
messages = [[NSMutableArray alloc] init];
self.tView.delegate = self;
self.tView.dataSource = self;
}
With these implementations:
- (void) initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"localhost", 80, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void) decoupleStreamsFromMainThread
{
inputStream.delegate = nil;
outputStream.delegate = nil;
[inputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
[outputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
}
- (void) spoolUpNetworkThread
{
[NSThread detachNewThreadSelector: #selector(networkThreadMain) toTarget: self withObject: nil];
}
- (void) networkThreadMain
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
static dispatch_once_t once;
dispatch_once(&once, ^{
[self rescheduleThreads];
((ChatClientAppDelegate * )[[UIApplication sharedApplication] delegate]).networkThread = [NSThread currentThread];
[inputStream setDelegate:self];
[outputStream setDelegate:self];
NSLog(#"inputStream status is: %#", [NSInputStream streamStatusCodeDescription: [inputStream streamStatus]]);
NSLog(#"outputStream status is: %#", [NSOutputStream streamStatusCodeDescription: [outputStream streamStatus]]);
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantFuture]];
});
[pool release]; // Release the objects in the pool.
}
- (void) rescheduleThreads
{
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
Any ideas on what might be wrong? Thanks in advance!
Something along the lines of the code in the question below works well ( I have similar code in my app):
How to properly open and close a NSStream on another thread