Why AsyncSocket can't connect to host when using dispatch_async() method? - ios

I'm a little confused when using dispatch_async method to use AsyncSocket.
Here's my code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_socket = [[AsyncSocket alloc] initWithDelegate:self];
[_socket connectToHost:#"192.168.1.122" onPort:9501 error:nil];
[self sendCurrentLocation];
});
I use this code to connect, but It didn't work, event - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err isn't invoked.
Anybody knows why? thanks.
p.s. forgive my English.

AsyncSocket data transmission is asynchronous, it is placed data in the queue, use threads to send, so we do not need to do it again asynchronously.

Related

NSURLConnection started in another thread. Delegate methods not called

I start a NSURLConnection in another thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^{
NSURLConnection *connection = [NSURLConnection connectionWithRequest:[request preparedURLRequest] delegate:self];
[connection start];
});
But my delegate method is not called:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data;
When run on the main thread everything is fine. How can I run connection on another thread and get the delegate methods called at the same thread too?
GCD creates, destroys, reuses threads implicitly and there is a chance that the thread you call start from will stop existing immediately afterwards. This may result in the delegate not receiving any callbacks.
If you would like to receive callback in background thread, you can use setDelegateQueue or sendAsynchronousRequest:queue:completionHandler: method:
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:NO];
[connection setDelegateQueue:[[NSOperationQueue alloc] init]];
[connection start];
The easiest way to start NSURLConnection in the background thread via GCD is:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
NSURLResponse* response = nil;
NSError* error = nil;
[NSURLConnection sendSynchronousRequest:request] returningResponse:&response error:&error];
NSLog(#"%#", response);
});
Yes, this is well known behavior of NSURLConnection because it needs a run loop to process the delegate events. The most common solution is (a) instantiate it with initWithRequest:delegate:startImmediately: where startImmediately is FALSE; (b) manually scheduleInRunLoop:forMode: to schedule it in the main run loop; and then (c) start the connection.
But, as you have it here, there's no point in dispatching this to a background queue, as it's already asynchronous so you should just initiate this from the main queue and none of the above is necessary. You use the above pattern in special cases (e.g. you were using NSOperation subclass to manage your requests), but generally it's not needed.
Also, FYI, effective iOS9, NSURLConnection is deprecated, so you should be using NSURLSession, anyway. And NSURLSession doesn’t suffer this limitation.
I had a similar issue. What I'm doing now is running NSURLConnection request in the main thread - it is running asynchronously so it won't slow down your application. In connectionDidFinishLoading, I run the following code to process the results of my calls. I perform the check because I have NSURLConnection call which may trigger other network calls. Since they are already running on a background thread I don't want to start a new one.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if ([NSThread isMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
[self processFinishLoading:connection];
});
}
else {
[self processFinishLoading:connection];
}
}

Equivalant in Swift performSelectorOnMainThread waitUntilDone

I wanted to translate this in swift :
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: googleRequestURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
It's for using google places api.
I wondering about using a simple NSURLSession request but it seems that the dataWithContentsOfURL do the job of an NSURLSession request ?
Someone ?
dataWithContentsOfURL is discouraged. You should use NSURLSession for asynchronous downloads, or if you prefer the simpler NSURLConnection.
The delegate callbacks tell the main thread when the download is finished - so no need to engage with Great Central Dispatch APIs.
Mundi is right that the better tool here is NSURLSession. But your code can work; just need to use GCD correctly, and deal with the fact that it might fail:
dispatch_async(kBgQueue) {
if let data = NSData.dataWithContentsOfURL(googleRequestURL) {
dispatch_sync(dispatch_get_main_queue()) { self.fetchedData(data) }
} else {
// Here's the problem with dataWithContentsOfURL. You had an error, but you
// don't know what it was. I guess you'll do something here...
}
}

dispatch_async NSURLConnection - download don't start

I'm trying to download information in background from a url.
I've read about GCD, Runloops and threads and decided that dispatc_async is my way to go.
After receiving data I aalso want to update the gui.
But... the NSUrlConnection don't seem to start at all. The delegate don't receive any calls.
I'v used this NSUrlRequest and NSUrlConnection in a synchronous way and the delegate got the data excpected.
Here is my code, a method in a viewcontroller;
- (void)dispatch: (NSURLRequest *) pRequest respondTo: (VivaQuery *) pQuery {
dispatch_queue_t downloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(downloadQueue, ^{
NSURLConnection *tConnectionResponse =[[NSURLConnection alloc] initWithRequest: pRequest delegate: pQuery];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Got to main thread.");
[pQuery requestEnd]; // Will update gui, i e aUIView setNeedsDisplay
});
});
}
Anyone got an idea?
Thanks in advance.
Kind regards,
Jan Gifvars
Stockholm
NSUrlConnection does its work asynchronously on it's own background thread so you do not need to create it in a background thread.

Synchronous communication using GCDAsyncSocket

I am using GCDAsyncSocket (CocoaAsyncSocket) for the socket communication in my app. Due to the asynchronous nature of GCDAsyncSocket, my network request (submitMessage below) is decoupled from the callback block that runs when data is received (socket:didReadData).
- (void)submitMessage:(NSDictionary *)messageObject onCompletion:(completionBlock)block {
...
[_socket writeData:requestData withTimeout:self.timeout tag:0];
[_socket readDataToLength:4 withTimeout:self.timeout tag:TAG_HEADER];
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
...
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
if (self.completionBlock != nil)
self.completionBlock(responseObject);
}
}
This approach works fine for one-off exchanges. But there are some cases when I need to post a request, then using the received data, post another request. I can't get this to work properly. Basically, I need something like this:
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
(...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
(...callback 2...)
}];
}];
or
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
(...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
(...callback 2...)
}];
where the order is strictly request1 - callback1 - request2 - callback2.
So the question is, how can I block the second request to run after the callback of the first request? Would GCD (dispatch_sync?) be the way to go?
Edit
I ended up using a solution similar to what #tigloo suggested (hence accepting his answer), but using NSCondition instead of GCD (if anyone's interested in details, I followed this great discussion). I am already running multiple threads (UI in main, high-level socket comms in another thread, and the socket operations in a third thread). Setting a class property and using NSCondition to lock the GCDAsyncSocket delegate until the response arrive seems the cleanest approach.
I think you were almost there. What about
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
// here, do something with response1 and create request2...
// then you can make request2 directly at the end of the callback:
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
// here, do something with response2...
}];
}];
No need for the GCD directives, no need to block execution (which is a bad practice anyway). Does this solve your problem?
The easiest approach is to append your requests to a serial dispatch queue and then wait for them to be completed by using dispatch_sync(). A discussion on StackOverflow can be found here.
The actual way of implementing it is up to your preferences. A possible idea is the following:
Create a new class "SyncRequest"
This class ideally has a private property of type bool "requestFinished", initialized to NO in the class' init method
In a method such as "sendSyncRequest" you call submitMessage:completionBlock:
The completion block will set the "requestFinished" property to YES
The last line in "sendSyncRequest" will be dispatch_sync(syncRequestQueue, ^(void){while(!requestFinished);});
This way you can construct multiple instances of SyncRequest, each handling a synchronized request. Rough sketch implementation:
#interface SyncRequest
#property bool requestFinished;
#end
#implementation SyncRequest
dispatch_queue_t syncRequestQueue;
-(id)init
{
self = [super init];
if ( !self )
return nil;
self.requestFinished = NO;
syncRequestQueue = dispatch_queue_create("com.yourid.syncrequest", DISPATCH_QUEUE_SERIAL);
return self;
}
-(void) sendSyncRequest:(NSDictionary*)messageObject
{
// submit message here and set requestFinished = YES in completion block
// wait for completion here
dispatch_sync(syncRequestQueue, ^(void){while(!self.requestFinished);});
}
#end
NOTE: I wrote the code without having the compiler at hand, you may have to create an indirect reference to "self" in the dispatch_sync call in order to avoid a cyclic reference.

iOS, NSURLConnection: Delegate Callbacks on Different Thread?

How can I get NSURLConnection to call it's delegate methods from a different thread instead of the main thread. I'm trying to mess around with the scheduleInRunLoop:forMode:but doesn't seem to do what I want.
I have to download a large file and it interrupts the main thread so frequently that some rendering that is happening starts getting choppy.
NSURLRequest * request = [NSURLRequest requestWithURL:url];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
NSRunLoop * loop = [NSRunLoop currentRunLoop];
NSLog(#"loop mode: %#",[loop currentMode]);
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
The other thing I don't see much of is "Modes" There are only two modes documented so not much really to test with.
Any ideas?
Thanks
There are several options:
In your implementation of the delegate methods, make use of dispatch_async.
Start the schedule the connection on a background thread.
You can do the latter like this:
// all the setup comes here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[connection scheduleInRunLoop:loop forMode:NSRunLoopCommonModes];
[loop run]; // make sure that you have a running run-loop.
});
If you want a guarantee on which thread you're running, replace the call to dispatch_get_global_queue() appropriately.
If you want to perform downloads on a separate thread, I'm pretty sure these are the droids you're looking for...
- (void) dispatchRequest{
self->finished = NO;
NSMutableURLRequest* request = //Formulate your request
NSThread* download_thread = [[NSThread alloc] initWithTarget:self selector:#selector(downloadThreadLoop:) object:request];
[download_thread start];
}
- (void) downloadThreadLoop:(NSMutableURLRequest*) request{
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
while(!self->finished]){
//This line below is the magic!
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
//...
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//...
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
//...
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
//...
self->finished = YES;
}
If you truly need to do the download in a new thread, it may be easier to detachNewThreadSelector:toTarget:withObject:, setup (and destroy) an NSAutoreleasePool, and then use one of the synchronous selectors like NSData's dataWithContentsOfURL:. This will not make use of the asynchronous NSURLConnectionDelegate.
Because this call is synchronous, it will not return until the file has been downloaded, which will block the main thread, but because you're in a new thread, it won't. Please note that this is typically discouraged behavior. Is there other code happening in the main thread that can be optimized?
NSURLConnection is already doing the download off of the main thread asynchronously. If I understand your question, you would like the delegate messages to be sent on a thread other than the main thread? You can't do that as you can't modify the internal implementation of NSURLConnection. I can think of two ways to simulate this.
Create a sublcass of NSURLConnection (e.g. MyURLConnection) that assigns itself as own delegate. Note that this creates an intentional retain cycle so be careful. MyURLConnection should define a new delegate that supports NSURLConnectionDelegate. Let's call this finalDelegate. When MyURLConnection handles it's own delegate messages, forward or dispatch them to finalDelegate on whatever thread you like.
Similar to option #1 but without the subclass. Handle the NSURLConnection delegate methods on the main thread and forward/dispatch them to whatever thread you like.
The main difference is if you want a reusable subclass that behaves this way or it's a one off implementation.
EDIT: added suggestion on how to run code in the background
If you are going to process the response in the background I would either use operations or grand central dispatch. No need to mess around with run loops and creating threads. Check out Apple's Concurrency Programming Guide.

Resources