I'm trying to set up a TCP connection to a local server, and since pretty much everyone agrees that Asyncsocket is the way to go, I went for it, too. However, I'm running into problems at the most basic level: the Asyncsocket instance doesn't have a local or remote address. I don't have much code, but this is what I do have:
- (void)viewDidLoad {
[super viewDidLoad];
AsyncSocket *socket = [[AsyncSocket alloc] initWithDelegate:self];
[socket connectToHost:#"www.google.com" onPort:80 error:nil];
}
- (BOOL)onSocketWillConnect:(AsyncSocket *)sock {
NSLog(#"%#", sock);
return YES;
}
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {
NSLog(#"%#", host);
}
The following shows up in the console (from the NSLog in onSocketWillConnect):
AsyncSocket 0x298de0 local nowhere remote nowhere has queued 0 reads 0 writes, no current read, no current write, read stream 0x299720 not open, write stream 0x299aa0 not open, not connected
Where it says "nowhere", of course it should have IP addresses, but it doesn't. From examples online it looks like my code is fine, yet I get this "nowhere" thing when running on two computers and my phone, so I'm probably doing something wrong. Anybody have an idea?
Thanks!
edit: For clarification: if I use error reporting (by passing an NSError instance to "connectToHost:onPort:error" and NSLogging that), all I get back is (null).
Wow, that's embarrassing. I needed to make an ivar to store the AsyncSocket, because without it, it went out of scope after viewDidLoad completed, which was why my onSocket:didConnectToHost:port wasn't called.
Related
I use the SocketRocket library for Objective-C to connect to a websocket:
-(void)open {
if( self.webSocket ) {
[self.webSocket close];
self.webSocket.delegate = nil;
}
self.webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"ws://192.168.0.254:5864"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20]];
self.webSocket.delegate = self;
[self.webSocket open];
}
Opening the connection works totally fine. The delegate is called after the connection was established.
-(void)webSocketDidOpen:(SRWebSocket *)webSocket {
NSLog(#"WebSocket is open");
}
But when I want to close the connection, nothing happens.
-(void)close {
if( !self.webSocket )
return;
[self.webSocket close];
self.webSocket.delegate = nil;
}
The delegate for successfully closing the connection is not called. Can anyone tell me why this happens?
Thank you for reading my question.
I figured out that the delegate is never called, because the websocket is never really closed. The closing of the websocket in the SRWebSocket happens in the method pumpWriting like this:
if (_closeWhenFinishedWriting &&
_outputBuffer.length - _outputBufferOffset == 0 &&
(_inputStream.streamStatus != NSStreamStatusNotOpen &&
_inputStream.streamStatus != NSStreamStatusClosed) &&
!_sentClose) {
_sentClose = YES;
[_outputStream close];
[_inputStream close];
if (!_failed) {
dispatch_async(_callbackQueue, ^{
if ([self.delegate respondsToSelector:#selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
[self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES];
}
});
}
_selfRetain = nil;
NSLog(#" Is really closed and released ");
}
else {
NSLog(#" Is NOT closed and released ");
}
All streams and an object to retain the websocket are closed or deleted there. As long as they are still open, the socket won´t be closed appropriately. But the closing never happened in my program, because when I tried to close the websocket, _closeWhenFinishedWriting was always NO.
This boolean is only set once in the disconnect method.
- (void)_disconnect;
{
assert(dispatch_get_current_queue() == _workQueue);
SRFastLog(#"Trying to disconnect");
_closeWhenFinishedWriting = YES;
[self _pumpWriting];
}
But when calling the closeWithCode method in SRWebSocket, disconnect is only called in one case and that is, if the websocket is in the connecting state.
BOOL wasConnecting = self.readyState == SR_CONNECTING;
SRFastLog(#"Closing with code %d reason %#", code, reason);
dispatch_async(_workQueue, ^{
if (wasConnecting) {
[self _disconnect];
return;
}
This means, if the socket is in another state, the websocket will never really close. One workaround is to always call the disconnect method. At least it worked for me and everything seems to be alright.
If anyone has an idea, why SRWebSocket is implemented like that, please leave a comment for this answer and help me out.
I think this is a bug.
When calling close, the server echo's back the 'close' message.
It is received by SRWebSocket, however the _selfRetain is never set to nil, and the socket remains open (the streams are not closed) and we have a memory leak.
I have checked and observed this in the test chat app as well.
I made the following change:
-(BOOL)_innerPumpScanner {
BOOL didWork = NO;
if (self.readyState >= SR_CLOSING) {
[self _disconnect]; // <--- Added call to disconnect which releases _selfRetain
return didWork;
}
Now the socket closes, the instance is released, and the memory leak is gone.
The only thing that I am not sure of is if the delegate should be called when closing in this way. Will look into this.
Once an endpoint has both sent and received a Close control frame, that endpoint SHOULD Close the WebSocket Connection as defined in Section 7.1.1. (RFC 6455 7.1.2)
The SRWebSocket instance doesn't _disconnect here because that would close the TCP connection to the server before the client has received a Close control frame in response. In fact, _disconnecting here will tear down the TCP socket before the client can even send its own Close frame to the server, because _disconnect ultimately calls _pumpWriting before closeWithCode: can. The server will probably respond gracefully enough, but it's nonconforming, and you won't be able to send situation-unique close codes while things are set up this way.
This is properly dealt with in handleCloseWithData:
if (self.readyState == SR_OPEN) {
[self closeWithCode:1000 reason:nil];
}
dispatch_async(_workQueue, ^{
[self _disconnect];
});
This block handles Close requests initiated by both the client and the server. If the server sends the first Close frame, the method runs as per the sequence you laid out, ultimately ending up in _pumpWriting via closeWithCode:, where the client will respond with its own Close frame. It then goes on to tear down the connection with that _disconnect.
When the client sends the frame first, closeWithCode: runs once without closing the TCP connection because _closeWhenFinishedWriting is still false. This allows the server time to respond with its own Close frame, which would normally result in running closeWithCode: again, but for the following block at the top of that method:
if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) {
return;
}
Because the readyState is changed on the first iteration of closeWithCode:, this time it simply won't run.
emp's bug fix is necessary to make this work as intended, however: otherwise the Close frame from the server doesn't do anything. The connection will still end, but dirtily, because the server (having both sent and received its frames) will break down the socket on its end, and the client will respond with an NSStreamEventEndEncountered:, which is normally reserved for stream errors caused by sudden losses of connectivity. A better approach would be to determine why the frame never gets out of _innerPumpScanner to handleCloseWIthData:. Another issue to keep in mind is that by default, close just calls closeWithCode: with an RFC-nonconforming code of -1. This threw errors on my server until I changed it to send one of the accepted values.
All that said: your delegate method doesn't work because you're unsetting the delegate right after you call close. Everything in close is inside an async block; there won't be a delegate left to call by the time you invoke didCloseWithCode: regardless of what else you do here.
I'm writing a little application for osx to listen and send commands over a serial connection to an Arduino interface. That works great using AMSerialPort.
But now I need to forward some data to a second Arduino, so I need a second serial connection.
Is this possible with this framework? I've searched almost everywhere how to open a second port and get informed through the delegates.
I can't really answer this question with regard to AMSerialPort. Though I've used AMSerialPort a lot in the past, it's been a while, and I've never tried to open two ports. However, I've just released my own Objective-C serial port library that definitely supports opening multiple ports simultaneously. It's called ORSSerialPort and you can get it here: https://github.com/armadsen/ORSSerialPort .
There's no trick to opening two serial ports with ORSSerialPort. Use the ORSSerialPortManager's availablePorts property to get all the available ports on the system. In the serialPort:didReceiveData: delegate method, you can check which port received the data. Something like this:
NSArray *availablePorts = [[ORSSerialPortManager sharedSerialPortManager] availablePorts];
// The next two lines are simplified by assuming availablePorts has two ports
// and you don't care which is which. Real code will be more sophisticated
self.port1 = [availablePorts objectAtIndex:0];
self.port2 = [availablePorts objectAtIndex:1];
self.port1.delegate = self;
self.port2.delegate = self;
[self.port1 open];
[self.port2 open];
- (void)serialPort:(ORSSerialPort *)port didReceiveData:(NSData *)data
{
if (port == self.port1) {
// Data is from port1
} else if (port == self.port2) {
// Data is from port2
}
}
I have an app that successfully uses the synchronous methods to download files (NSData's initWithContentsOfURL and NSURLConnection's sendSynchronousRequest), but now I need to support large files. This means I need to stream to disk bit by bit. Even though streaming to disk and becoming asynchronous should be completely orthoganal concepts, Apple's API forces me to go asynchronous in order to stream.
To be clear, I am tasked with allowing larger file downloads, not with re-architecting the whole app to be more asynchronous-friendly. I don't have the resources. But I acknowledge that the approaches that depend on re-architecting are valid and good.
So, if I do this:
NSURLConnection* connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
.. I eventually have didReceiveResponse and didReceiveData called on myself. Excellent. But, if I try to do this:
NSURLConnection* connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
while( !self.downloadComplete )
[ NSThread sleepForTimeInterval: .25 ];
... didReceiveResponse and didReceiveData are never called. And I've figured out why. Weirdly, the asynchronous download happens in the same main thread that I'm using. So when I sleep the main thread, I'm also sleeping the thing doing the work. Anyway, I have tried several different ways to achieve what I want here, including telling the NSURLConnection to use a different NSOperationQueue, and even doing dispatch_async to create the connection and start it manually (I don't see how this couldn't work - I must not have done it right), but nothing seems to work. Edit: What I wasn't doing right was understanding how Run Loops work, and that you need to run them manually in secondary threads.
What is the best way to wait until the file is done downloading?
Edit 3, working code:
The following code actually works, but let me know if there's a better way.
Code executing in the original thread that sets up the connection and waits for the download to complete:
dispatch_queue_t downloadQueue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
dispatch_async(downloadQueue, ^{
self.connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
[ [ NSRunLoop currentRunLoop ] run ];
});
while( !self.downloadComplete )
[ NSThread sleepForTimeInterval: .25 ];
Code executing in the new thread that responds to connection events:
-(void)connection:(NSURLConnection*) connection didReceiveData:(NSData *)data {
NSUInteger remainingBytes = [ data length ];
while( remainingBytes > 0 ) {
NSUInteger bytesWritten = [ self.fileWritingStream write: [ data bytes ] maxLength: remainingBytes ];
if( bytesWritten == -1 /*error*/ ) {
self.downloadComplete = YES;
self.successful = NO;
NSLog( #"Stream error: %#", self.fileWritingStream.streamError );
[ connection cancel ];
return;
}
remainingBytes -= bytesWritten;
}
}
-(void)connection:(NSURLConnection*) connection didFailWithError:(NSError *)error {
self.downloadComplete = YES;
[ self.fileWritingStream close ];
self.successful = NO;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.downloadComplete = YES;
[ self.fileWritingStream close ];
self.successful = YES;
}
... didReceiveResponse and didReceiveData are never called. And I've
figured out why. Weirdly, the asynchronous download happens in the
same main thread that I'm using. It doesn't create a new thread. So
when I sleep the main thread, I'm also sleeping the thing doing the
work.
Exactly. The connection is driven by the run loop; if you sleep the thread, the run loop stops, and that prevents your connection from doing its thing.
So don't do anything special. Let the app sit there, with the run loop running. Maybe put a little spinner on the screen to entertain the user. Go about your business if you can. If at all possible, let the user continue to use the application. Your delegate method will be called when the connection is complete, and then you can do what you need to do with the data.
When you move your code to a background thread, you'll again need a run loop to drive the connection. So you'll start create a run loop, schedule your connection, and then just return. The run loop will keep running, and your delegate method will again be called when the connection completes. If the thread is done, you can then stop the run loop and let the thread exit. That's all there is to it.
Example: Let's put this in concrete terms. Let's say that you want to make a number of connections, one at a time. Stick the URL's in a mutable array. Create a method called (for example) startNextConnection that does the following things:
grabs an URL from the array (removing it in the process)
creates an URL request
starts a NSURLConnection
return
Also, implement the necessary NSURLConnectionDelegate methods, notably connectionDidFinishLoading:. Have that method do the following:
stash the data somewhere (write it to a file, hand it to another thread for parsing, whatever)
call startNextConnection
return
If errors never happened, that'd be enough to retrieve the data for all the URLs in your list. (Of course, you'll want startNextConnection to be smart enough to just return when the list is empty.) But errors do happen, so you'll have to think about how to deal with them. If a connection fails, do you want to stop the entire process? If so, just have your connection:didFailWithError: method do something appropriate, but don't have it call startNextConnection. Do you want to skip to the next URL on the list if there's an error? Then have ...didFailWithError: call startNextRequest.
Alternative: If you really want to keep the sequential structure of your synchronous code, so that you've got something like:
[self downloadURLs];
[self waitForDownloadsToFinish];
[self processData];
...
then you'll have to do the downloading in a different thread so that you're free to block the current thread. If that's what you want, then set up the download thread with a run loop. Next, create the connection using -initWithRequest:delegate:startImmediately: as you've been doing, but pass NO in the last parameter. Use -scheduleInRunLoop:forMode: to add the connection to the download thread's run loop, and then start the connection with the -start method. This leaves you free to sleep the current thread. Have the connection delegate's completion routine set a flag such as the self.downloadComplete flag in your example.
I hesitate to provide this answer because the others are correct that you really should structure your app around the asynchronous model. Nevertheless:
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
NSString* myPrivateMode = #"com.yourcompany.yourapp.DownloadMode";
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:myPrivateMode];
[connection start];
while (!self.downloadComplete)
[[NSRunLoop currentRunLoop] runMode:myPrivateMode beforeDate:[NSDate distantFuture]];
Do not do this on the main thread. Your app is just as likely to be terminated for blocking the main thread as for downloading too big a file to memory.
By the way, given that you're downloading to a file instead of memory, you should consider switching from NSURLConnection to NSURLDownload.
I think your sleepForInterval is blocking the NSURLConnection's activity -
No run loop processing occurs while the thread is blocked.
From the NSThread documentation.
I think you might have to rethink how you're setting your downloadComplete variable. Consider using your connectionDidFinishLoading:connection delegate method to determine when the download is complete instead of your loop + sleep?
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.downloadComplete = YES;
// release the connection, and the data object
[connection release];
[receivedData release];
}
From the NSURLConnection guide.
You can use the connection:connection didFailWithError:error delegate method to ensure you're dealing with situations where the download does not complete.
I'm currently using NSURLConnection to test if I can successfully connect to a server. Up until now, everything has worked flawlessly. The delegate methods get called, I can do what i want. But when I want to set a BOOL called connected in.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.connected = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.connected = NO;
}
I always get NO.
I'm using my connection class in another class. I access my variable by doing this.
Connection *connection = [[Connection alloc] init];
[connection connectTo:#"localhost"];
connection.connected;<------ this is always NO.
Thanks
Your connectTo method is being called asynchronously. Meaning, after calling connectTo, the execution will proceed immediately to the next line where you check for your connected property. It won't wait for the connection to receive a response.
Also, you may want to take a look at Apple's Reachability class, which is provided for exactly this sort of connection testing:
http://developer.apple.com/library/ios/#samplecode/Reachability
This is with reference to the StackOverflow question Managing multiple asynchronous NSURLConnection connections
I have multiple asynchronous HTTP requests being made at the same time. All these use the same NSURLConnection delegate functions. (The receivedData object is different for each connection as specified in the other question above. In the delegate, I parse the receivedDate object, and do additional operations on those parsed strings)
Everything works fine for me so far, but I'm not sure if I need to do anything to ensure correct “multithreaded” behavior.
Is it possible that more than two connections will use the delegate at the same time? (I would think yes)
If yes, how is it resolved? (Does Cocoa do this automatically?)
Do I need to have additional checks in place to ensure that each request is handled “correctly”?
I enhanced the Three20 library to implement asynchronous connections across multiple threads in order to fetch data even if the user was playing with the UI. After many hours of chasing down random memory leaks that were detected within the CFNetwork framework I finally root caused the issue. I was occasionally losing track of responses and data.
Any data structures which are accessed by multiple threads must be protected by an appropriate lock. If you are not using locks to access shared data structures in a mutually exclusive manner then you are not thread safe. See the "Using Locks" section of Apple's Threading Programming Guide.
The best solution is to subclass NSURLConnection and add instance variables to store its associated response and response data. In each connection delegate method you then cast the NSURLConnection to your subclass and access those instance variables. This is guaranteed to be mutually exclusive because every connection will be bundled with its own response and data. I highly recommend trying this since it is the cleanest solution. Here's the code from my implementation:
#interface TTURLConnection : NSURLConnection {
NSHTTPURLResponse* _response;
NSMutableData* _responseData;
}
#property(nonatomic,retain) NSHTTPURLResponse* response;
#property(nonatomic,retain) NSMutableData* responseData;
#end
#implementation TTURLConnection
#synthesize response = _response, responseData = _responseData;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate {
NSAssert(self != nil, #"self is nil!");
// Initialize the ivars before initializing with the request
// because the connection is asynchronous and may start
// calling the delegates before we even return from this
// function.
self.response = nil;
self.responseData = nil;
self = [super initWithRequest:request delegate:delegate];
return self;
}
- (void)dealloc {
[self.response release];
[self.responseData release];
[super dealloc];
}
#end
/////////////////////////////////////////////////////////////////
////// NSURLConnectionDelegate
- (void)connection:(NSURLConnection*)connection
didReceiveResponse:(NSHTTPURLResponse*)response {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
ttConnection.response = response;
ttConnection.responseData = [NSMutableData
dataWithCapacity:contentLength];
}
- (void)connection:(NSURLConnection*)connection
didReceiveData:(NSData*)data {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
[ttConnection.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
if (ttConnection.response.statusCode == 200) {
// Connection success
}
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
// Handle the error
}
Assuming you're launching all of the (asynchronous) connections on a single thread, then the delegate messages will all get posted in that thread's run loop. Therefore the delegate only needs to be able to deal with one message being handled at once; the run loop will hand one message off at a time. This means that while the order of the delegate messages is unknown and the next message could come from any connection object, there will be no concurrent execution of your delegate methods.
However, were you actually trying to use the same delegate object across multiple threads, rather than just using the asynchronous nature of the API, then you would need to deal with concurrent delegate methods.
Yes it's possible to have multiple connections. the notification object contains a pointer to the NSURLConnection that triggered the notification.
Internally I guess NSURLConnection listens to a socket and does something like this when it has data ready.
[your_delegate
performSelectorOnMainThread:#selector(connectionCallback:)
withObject:self
waitUntilDone:NO];
so you don't have to worry about it being multithreaded, NSURLConnection will take care of this. For simplicity I have written self, in the real world a NSNotification object is given.
You shouldn't have to do any checks related to multithreading.