NSConnection - how to properly do "unvending" of an object? - objective-c

For Mac OSX, I'm trying to use an NSConnection to proxy access of an object from one application instance to another on the same host. The relevant code is below. I can provide more if needed. Assume when I say "server", I'm referring to the application that actually "vends" an object with an NSConnection. And "client" is the other instance of the same application that gets a proxy to it.
Everything works, except for two issues.
When an application acting as a server attempts to tear down the object it is vending, any client connected through a proxy still remains. That is, even after I call my stopLocalServer function below, any client app that previously connected and got a proxy object is still able to send messages and invoke code on the server app. I would have expected the client to throw an exception when passing a message after the server calls NSConnection:invalidate. How do I forcibly disconnect any client without requiring the server process to exit?
In the startClientConnection code below, if the server never vended the object in the first place with the expected registered name, then the call on the client to NSConnection:connectionWithRegisteredName:host will immediately return nil. This is good. But if the server had started vending an object via the startLocalServer code below, then later stops vending it with stopLocalServer, subsequent client attempts to connect will hang (block forever) until the server application process exits. The call to NSConnection:connectionWithRegisteredName returns a non-nil object, but the call to [_clientConnection rootProxy] hangs forever until the server application actually exits.
I suspect that I'm not properly tearing down the original NSConnection object or I'm missing something basic here.
Here is some relevant code for the platform that my user interface code sits on top of:
-(void)startLocalServer:(NSString*)str
{
[self stopLocalServer]; // clean up any previous instance that might be running
_serverConnection = [NSConnection new];
[_serverConnection setRootObject:self];
[_serverConnection registerName:str];
}
-(void)stopLocalServer
{
[_serverConnection registerName:nil];
[_serverConnection setRootObject:nil];
[_serverConnection invalidate];
_serverConnection = nil;
}
-(void)startClientConnection:(NSString*)str
{
[self stopClientConnection]; // tear down any previous connection
_clientConnection = [NSConnection connectionWithRegisteredName:str host:nil];
if ((_clientConnection == nil) || (_clientConnection.valid == NO))
{
LogEvent(#"ERROR - _clientConnection is nil or invalid!");
}
else
{
_proxy = [_clientConnection rootProxy];
}
}
-(void)stopClientConnection
{
_proxy = nil;
[_clientConnection invalidate];
_clientConnection = nil;
}

Answering my own question. I'll still hold out for a better answer, or if anyone can do a better job explaining the reasoning about why this is needed.
I believe the stopLocalServer function needs to call [[_serverConnection receivePort] invalidate] such that the port created with the connection is closed. Just adding that line to the original stopLocalServer function solves my problem. That prevents further connection attempts and messages from succeeding.
More appropriately, the application call can just own the port that the NSConnection uses. So this becomes a better solution for starting and stopping a distributed object listener:
-(void)startLocalServer:(NSString*)str
{
[self stopLocalServer]; // clean up any previous instance that might be running
_port = [NSPort port]; // _port is of type NSPort*
_serverConnection = [NSConnection connectionWithReceivePort:_port sendPort:nil];
[_serverConnection setRootObject:self];
[_serverConnection registerName:str];
}
-(void)stopLocalServer
{
[_serverConnection registerName:nil];
[_serverConnection setRootObject:nil];
[_serverConnection invalidate];
_serverConnection = nil;
[_port invalidate];
_port = nil;
}
That seems to solve both #1 and #2 above.

Related

After IORegisterForSystemPower failing to call IODeregisterForSystemPower

I have an application, written in Objective-C for MacOS 10.10+ which registers for sleep/wake notifications (code sample below, but the code isn't the question). What I am wondering is, if I call IORegisterForSystemPower at App initialisation, but during debugging I kill the app before it has a chance to call IODeregisterForSystemPower, what are the implications? Does the app get de-registered automatically when it dies in any case? Is there a system dictionary I need to clear out (a plist somewhere, etc.)? Thanks in advance for any help.
io_object_t root_notifier = MACH_PORT_NULL;
IONotificationPortRef notify = NULL;
DebugLog(#"App: Logging IORegisterForSystemPower sleep/wake notifications %#", [NSDate date]);
/* Log sleep/wake messages */
powerCallbackPort = IORegisterForSystemPower ((__bridge void *)self, &notify, sleepWakeCallback, &root_notifier);
if ( powerCallbackPort == IO_OBJECT_NULL ) {
DebugLog(#"IORegisterForSystemPower failed");
return;
}
self.rootNotifierPtr = &(root_notifier); // MARK: deregister with this pointer
if ( notify && powerCallbackPort )
{
CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
}
To be honest, I don't know the exact answer. But it may help you.
First of, if you call IORegisterForSystemPower, you need to make two calls in this order: - Call IODeregisterForSystemPower with the 'notifier' argument returned here. - Then call IONotificationPortDestroy passing the 'thePortRef' argument returned here (Please visit apple's document for more detail).
In case of port binding, if I use CFSocketSetAddress, before releasing this socket no other can use this port for binding. But in case of app terminate/closed without releasing this socket this port is available. That means after terminated the app system automatically releasing this.
Does the app get de-registered automatically when it dies in any case?
I think it will automatically de-registered by system.
I also used similar code as you in one of my project. But recently replaced with below codes:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: #selector(receiveWakeNotification:) name: NSWorkspaceDidWakeNotification object: nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: #selector(receiveSleepNotification:) name: NSWorkspaceWillSleepNotification object: nil];

How to debug communication between XPC service and client app in OSX

I'm trying to write a simple pair of "client app" & "XPC service". I was able to launch xpc service from client (i.e I can see service running in the Activity monitor processes list), but when I try to send any request, that has a response block, I get an error: "Couldn’t communicate with a helper application."
The worst thing here is that error doesn't give me any info about what went wrong. And I'm also unable to debug the service properly. As I understand, the correct way to do this is to attach a debugger to process (Debug->Attach to process, also see here). I have both client and service projects in a single workspace.
When I run client from xcode and try to attach debugger to launched service, that ends with a "Could not attach to pid : X" error.
If I archive the client app run it from app file and then try to attach debugger to service the result is the same.
The only way to record something from the service I could imagine is to write a logger class, that would write data to some file. Haven't tried this approach yet, however that looks insane to me.
So my question is:
a) How to find out what went wrong, when receiving such non-informative response like: "Couldn’t communicate with a helper application"?
b) And also, what's the correct way to debug the xpc service in the first place? The link above is 5 years old from now, however I can see that some people were saying that "attach to debugger" wasn't working.
The code itself is fairly simple:
XPC service, listener implementation:
#import "ProcessorListener.h"
#implementation ProcessorListener
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
[newConnection setExportedInterface: [NSXPCInterface interfaceWithProtocol:#protocol(TestServiceProtocol)]];
[newConnection setExportedObject: self];
self.xpcConnection = newConnection;
newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: #protocol(Progress)];
// connections start suspended by default, so resume and start receiving them
[newConnection resume];
return YES;
}
- (void) sendMessageWithResponse:(NSString *)receivedString reply:(void (^)(NSString *))reply
{
reply = #"This is a response";
}
- (void) sendMessageWithNoResponse:(NSString *)mString
{
// no response here, dummy method
NSLog(#"%#", mString);
}
And the main file for service:
#import <Foundation/Foundation.h>
#import "TestService.h"
#interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
#end
#implementation ServiceDelegate
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
// This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection.
// Configure the connection.
// First, set the interface that the exported object implements.
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(TestServiceProtocol)];
// Next, set the object that the connection exports. All messages sent on the connection to this service will be sent to the exported object to handle. The connection retains the exported object.
TestService *exportedObject = [TestService new];
newConnection.exportedObject = exportedObject;
// Resuming the connection allows the system to deliver more incoming messages.
[newConnection resume];
// Returning YES from this method tells the system that you have accepted this connection. If you want to reject the connection for some reason, call -invalidate on the connection and return NO.
return YES;
}
#end
int main(int argc, const char *argv[])
{
// [NSThread sleepForTimeInterval:10.0];
// Create the delegate for the service.
ServiceDelegate *delegate = [ServiceDelegate new];
// Set up the one NSXPCListener for this service. It will handle all incoming connections.
NSXPCListener *listener = [NSXPCListener serviceListener];
listener.delegate = delegate;
// Resuming the serviceListener starts this service. This method does not return.
[listener resume];
return 0;
}
For client app, the UI contains a bunch of buttons:
- (IBAction)buttonSendMessageTap:(id)sender {
if ([daemonController running])
{
[self executeRemoteProcessWithName:#"NoResponse"];
}
else
{
[[self.labelMessageResult cell] setTitle: #"Error"];
}
}
- (IBAction)buttonSendMessage2:(id)sender {
if ([daemonController running])
{
[self executeRemoteProcessWithName:#"WithResponse"];
}
else
{
[[self.labelMessageResult cell] setTitle: #"Error"];
}
}
- (void) executeRemoteProcessWithName: (NSString*) processName
{
// Create connection
NSXPCInterface * myCookieInterface = [NSXPCInterface interfaceWithProtocol: #protocol(Processor)];
NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: #"bunldeID"]; // there's a correct bundle id there, really
[connection setRemoteObjectInterface: myCookieInterface];
connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(Progress)];
connection.exportedObject = self;
[connection resume];
// NOTE that this error handling code is not called, when debugging client, i.e connection seems to be established
id<Processor> theProcessor = [connection remoteObjectProxyWithErrorHandler:^(NSError *err)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle: #"OK"];
[alert setMessageText: err.localizedDescription];
[alert setAlertStyle: NSAlertStyleWarning];
[alert performSelectorOnMainThread: #selector(runModal) withObject: nil waitUntilDone: YES];
}];
if ([processName containsString:#"NoResponse"])
{
[theProcessor sendMessageWithNoResponse:#"message"];
}
else if ([processName containsString:#"WithResponse"])
{
[theProcessor sendMessageWithResponse:#"message" reply:^(NSString* replyString)
{
[[self.labelMessageResult cell] setTitle: replyString];
}];
}
}
Jonathan Levin's XPoCe tool is helpful when you can't attach a debugger.
You can add logging NSLog() or fprintf(stderr,...) to your service and clients, specifically around the status codes. You just have to specify the path of the file to write stdout and stderr. <key>StandardErrorPath</key> <string>/tmp/mystderr.log</string>
There's a section on Debugging Daemons at this article on objc.io .

WebSocket Connection is not closing using SocketRocket

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.

Using blocks within blocks in Objective-C: EXC_BAD_ACCESS

Using iOS 5's new TWRequest API, I've ran into a brick wall related with block usage.
What I need to do is upon receiving a successful response to a first request, immediately fire another one. On the completion block of the second request, I then notify success or failure of the multi-step operation.
Here's roughly what I'm doing:
- (void)doRequests
{
TWRequest* firstRequest = [self createFirstRequest];
[firstRequest performRequestWithHandler:^(NSData* responseData,
NSHTTPURLResponse* response,
NSError* error) {
// Error handling hidden for the sake of brevity...
TWRequest* secondRequest = [self createSecondRequest];
[secondRequest performRequestWithHandler:^(NSData* a,
NSHTTPURLResponse* b,
NSError* c) {
// Notify of success or failure - never reaches this far
}];
}];
}
I am not retaining either of the requests or keeping a reference to them anywhere; it's just fire-and-forget.
However, when I run the app, it crashes with EXC_BAD_ACCESS on:
[secondRequest performRequestWithHandler:...];
It executes the first request just fine, but when I try to launch a second one with a handler, it crashes. What's wrong with that code?
The methods to create the requests are as simple as:
- (TWRequest*)createFirstRequest
{
NSString* target = #"https://api.twitter.com/1/statuses/home_timeline.json";
NSURL* url = [NSURL URLWithString:target];
TWRequest* request = [[TWRequest alloc]
initWithURL:url parameters:params
requestMethod:TWRequestMethodGET];
// _twitterAccount is the backing ivar for property 'twitterAccount',
// a strong & nonatomic property of type ACAccount*
request.account = _twitterAccount;
return request;
}
Make sure you're keeping a reference/retaining the ACAccountStore that owns the ACAccount you are using to sign the TWRequests.
If you don't, the ACAccount will become invalid and then you'll get EXC_BAD_ACCESS when trying to fire a TWRequest signed with it.
I'm not familiar with TW*, so consider this a wild guess ... try sending a heap-allocated block:
[firstRequest performRequestWithHandler:[^ (NSData *responseData, ...) {
...
} copy]];
To clarify, I think the block you're sending is heap-allocated, so while TW* might be retaining it, it won't make any difference if it has already gone out of scope.

asyncsocket "connectToHost" always succeeds and never returns a fail

I am creating a socket connection with objective C, using asyncsocket. I do this using the "connectToHost" method. I am trying to handle the case where the socket connection fails. "connectToHost" is supposed to return "YES" when it connects sucessfully, and a NO otherwise. For some reason, it is always returning a yes. I even supplied a blank string as the host and it still returns yes. Any thoughts?
Thanks,
Robin
BOOL connectStatus = NO; //used to check if connection attempt succeeded
testSocket = [[AsyncSocket alloc] initWithDelegate: self];
connectStatus = [testSocket connectToHost: #"" onPort: 5000 error: nil];
if(connectStatus == NO)
{
NSLog(#"Failed to connect to socket ");
}
else {
NSLog(#"Connected to socket sucessfully, connectStatus = %d", connectStatus);
}
Per the header file:
// Once one of the accept or connect methods are called, the AsyncSocket instance is locked in
// and the other accept/connect methods can't be called without disconnecting the socket first.
// If the attempt fails or times out, these methods either return NO or
// call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:".
If you check the source – for the love of all things holy, when working with open source software, USE THE SOURCE! –, you will see that the method you are invoking returns NO only when it fails to start the connection process. A return value of YES just says, "Okay, I'm trying to connect:
"If things go wrong, I'll let you know by calling onSocket:willDisconnectWithError: and onSocketDidDisconnect:.
"If all goes well, you'll get a onSocket:didConnectToHost:port: message, mmkay?"
Don't expect synchronous behavior from an asynchronous socket library.