How do I watch for file changes on OS X? - objective-c

I would like to be notified of writes into a given file – without polling, without having to read from the file and without having to monitor the parent directory and watch the file modification time stamps. How do I do that?

I couldn’t find a simple example, so I’m contributing what I came up with for future reference:
#interface FileWatch ()
#property(assign) dispatch_source_t source;
#end
#implementation FileWatch
#synthesize source;
- (id) initWithPath: (NSString*) path targetQueue: (dispatch_queue_t) queue block: (dispatch_block_t) handler
{
self = [super init];
int descriptor = open([path fileSystemRepresentation], O_EVTONLY);
if (descriptor < 0) {
return nil;
}
[self setSource:dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, descriptor, DISPATCH_VNODE_WRITE, queue)];
dispatch_source_set_event_handler(source, handler);
dispatch_source_set_cancel_handler(source, ^{
close(descriptor);
});
dispatch_resume(source);
return self;
}
- (void) dealloc
{
if (source) {
dispatch_source_cancel(source);
dispatch_release(source);
source = NULL;
}
}
#end

From my expericence, in some case files aren't only written but deleted then rewritten (the case for some plist files). Then you have to adapt the code a little bit : calling the method again when the files gets replaced in ordre to keep the monitoring.
- (void) myMonitoringMethodWithPath: :(NSString*) path
__block typeof(self) blockSelf = self;
__block dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fildes, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
queue);
dispatch_source_set_event_handler(source, ^
{
unsigned long flags = dispatch_source_get_data(source);
//Do some stuff
if(flags & DISPATCH_VNODE_DELETE)
{
[blockSelf myMonitoringMethodWithPath:path];
}
});
dispatch_source_set_cancel_handler(source, ^(void)
{
close(fildes);
});
dispatch_resume(source);
}

Related

NSOutputStream is always waiting for space available after second NSOperation tries to write to it

I am creating an NSOutputStream and passing it to an NSOperation, which I call from an NSOperationQueue. In the operation, I am polling for hasSpaceAvailable so that I can write out to the socket. This works fine the first time I write out to the socket. After the operation returns, however, and I try to write again to the socket, the write never goes through as I'm infinitely waiting for space to become available in the output socket. I've tried opening/closing the output stream each time I write, but have the same problem.
I open the output stream in the init function (the NSOutputStream is created from a Bluetooth EASession:
_commandSession = [[EASession alloc] initWithAccessory:self.selectedAccessory forProtocol:commandProtocolString];
_commandOutputStream = [_commandSession outputStream];
[_commandOutputStream open];
I also create the operation queue in the init:
_senderOperationQueue = [[NSOperationQueue alloc] init];
_senderOperationQueue.name = #"Send Queue";
_senderOperationQueue.maxConcurrentOperationCount = 1;
I have a text field with the data I want to send over the output stream. This function is called each time I click the send button:
-(void)sendCommandData:(NSData *)buf
{
_commandSendOperation =[[SenderOperation alloc] initWithStream:_commandOutputStream data:buf delegate:self];
[_senderOperationQueue addOperation:_commandSendOperation];
}
This is how my operation code looks like:
(SenderOperation.h)
#import <Foundation/Foundation.h>
#protocol SenderOperationDelegate;
#interface SenderOperation : NSOperation
{
NSOutputStream *_stream;
NSData *_sendData;
}
#property (nonatomic, assign) id <SenderOperationDelegate> delegate;
#property (nonatomic, retain) NSOutputStream *stream;
#property (nonatomic, retain) NSData *sendData;
- (id)initWithStream:(NSOutputStream *)stream data:(NSData *)buf delegate:(id<SenderOperationDelegate>)theDelegate;
#end
// Delegate to notify main thread the completion of a BT input buffer stream
#protocol SenderOperationDelegate <NSObject>
-(void)sendComplete:(SenderOperation *)sender;
#end
(SenderOperation.m)
#import "SenderOperation.h"
#implementation SenderOperation
#synthesize delegate = _delegate;
#synthesize stream = _stream;
#synthesize sendData = _sendData;
- (id)initWithStream:(NSOutputStream *)stream data:(NSData *)buf delegate:(id<SenderOperationDelegate>)theDelegate
{
if (self = [super init])
{
self.delegate = theDelegate;
self.stream = stream;
self.sendData = buf;
}
return self;
}
#define MAX_PACKET_SIZE 20
- (void)main
{
if (self.isCancelled)
return;
// total length of the data packet we need to send
int totalLength = [_sendData length];
// length of packet to send (given our upper bound)
int len = (totalLength <= MAX_PACKET_SIZE) ? totalLength:MAX_PACKET_SIZE;
// stream write response
int streamWriteResponse;
// bytes already written out to the output stream
int bytesWritten = 0;
while (1)
{
if (!len) break;
if ([self.stream hasSpaceAvailable])
{
streamWriteResponse = [self.stream write:[self.sendData bytes] maxLength:len];
if (streamWriteResponse == -1)
{
break;
}
bytesWritten += streamWriteResponse;
// update the data buffer with left over data that needs to be written
if (totalLength - bytesWritten)
self.sendData = [self.sendData subdataWithRange:NSMakeRange(bytesWritten, totalLength - bytesWritten)];
// update length of next data packet write
len = (totalLength - bytesWritten) >= MAX_PACKET_SIZE ? MAX_PACKET_SIZE : (totalLength - bytesWritten);
}
}
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(sendComplete:) withObject:self waitUntilDone:NO];
}
You'll need to use run-loop scheduling for your stream, rather than polling. Quoth the docs for -[EASession outputStream]:
This stream is provided for you automatically by the session object but you must configure it if you want to receive any associated stream events. You do this by assigning a delegate object to the stream that implements the stream:handleEvent: delegate method. You must then schedule the stream in a run loop so that it can send data asynchronously from one of your application’s threads.

Objective-C, Authorization fails to perform function

I'm trying to create an Authorization to copy a file using SMJobBless, although I can't get it to work. The helper app is successfully authorized and the Job is available! message appears before the [self copyFile] method, but the copyFile always fails. If someone could shed some light on what I'm doing wrong or provide and example of how to make this work that would be great.
appDelegate.h
#import <Cocoa/Cocoa.h>
#interface SMJobBlessAppController : NSObject {
IBOutlet NSTextField *_textField;
}
- (BOOL)blessHelperWithLabel:(NSString *)label error:(NSError **)error;
- (void)copyFile;
#end
appDelegate.m
#import <ServiceManagement/ServiceManagement.h>
#import <Security/Authorization.h>
#import "appDelegate.h"
#implementation SMJobBlessAppController
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSError *error = nil;
if (![self blessHelperWithLabel:#"com.apple.bsd.SMJobBlessHelper" error:&error]) {
NSLog(#"Something went wrong!");
} else {
/* At this point, the job is available. However, this is a very
* simple sample, and there is no IPC infrastructure set up to
* make it launch-on-demand. You would normally achieve this by
* using a Sockets or MachServices dictionary in your launchd.plist.
*/
NSLog(#"Job is available!");
[self->_textField setHidden:false];
[self copyFile];
}
}
- (void)copyFile {
NSError *error = nil;
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *sourceFile = #"~/path/to/file.txt";
NSString *destFile = #"~/Library/Application Support/myApp/file.txt";
if ([fileManager copyItemAtPath:sourceFile toPath:destFile error:&error] == YES) {
NSLog (#"[FILE] Copied.");
// NSLog (#"Copy successful");
} else {
NSLog (#"[FILE] Copy failed.");
NSLog (#" %# %#",sourceFile, destFile);
// NSLog (#"Copy failed");
}
[fileManager release];
return;
}
- (BOOL)blessHelperWithLabel:(NSString *)label error:(NSError **)error;
{
BOOL result = NO;
AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags flags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
AuthorizationRef authRef = NULL;
/* Obtain the right to install privileged helper tools (kSMRightBlessPrivilegedHelper). */
OSStatus status = AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &authRef);
if (status != errAuthorizationSuccess) {
NSLog(#"Failed to create AuthorizationRef, return code %i", status);
} else {
/* This does all the work of verifying the helper tool against the application
* and vice-versa. Once verification has passed, the embedded launchd.plist
* is extracted and placed in /Library/LaunchDaemons and then loaded. The
* executable is placed in /Library/PrivilegedHelperTools.
*/
result = SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, authRef, (CFErrorRef *)error);
}
return result;
}
#end
You're totally missing the point of SMJobBless. It doesn't magically make your current app able to do privileged things. Instead, it installs and runs a separate helper tool, which is allowed to do privileged things, but should do nothing else (as little as possible).
You need to move your code in copyFile to the main function in SMJobBlessHelper.c. (And since that's a C file, you'll have to either rewrite it in C -- perhaps using CoreFoundation -- or you'll have to change the tool to use Objective-C. Nobody said this would be easy.)

Recommended way to copy arbitrary files using Cocoa

I need to copy file from one OS X volume to another OS X volume. While an *.app isn't strictly speaking a file but a folder, user expect them to be a unit. Thus, if user selects a file, the app should not show its folder's contents, but copy it as a unit.
Therefore I ask, if there exists a recommended way to copy files using pure Cocoa code.
Optional: Which command line tool provides help and could be utilized by a Cocoa application.
NSFileManager is your friend:
NSError *error = nil;
if ([[NSFileManager defaultManager] copyItemAtPath:#"path/to/source" toPath:#"path/to/destination" error:&error])
{
// copy succeeded
}
else
{
// copy failed, print error
}
You can also use FSCopyObjectAsync function. You can display file copy progress and you can also cancel file copy using FSCopyObjectAsync().
Take a look at FSFileOperation example code.
This sample shows how to copy and move both files and folders. It
shows both the synchronous and asynchronous (using CFRunLoop) use of
the FSFileOperation APIs. In addition, it shows path and FSRef
variants of the API and how to get status out of the callbacks. The
API is conceptually similar to the FSVolumeOperation APIs introduced
in Mac OS X 10.2.
Example of FSCopyObjectAsync:
#import <Cocoa/Cocoa.h>
#interface AsyncCopyController : NSObject {
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object;
//delegate method
-(void)didReceiveCurrentPath : (NSString *)curremtItemPath bytesCompleted : (unsigned long long)floatBytesCompleted currentStageOfFileOperation : (unsigned long)stage;
-(void)didCopyOperationComplete : (BOOL)boolean;
-(void)didReceiveCopyError : (NSString *)Error;
-(void)cancelAllAsyncCopyOperation;
#end
#import "AsyncCopyController.h"
static Boolean copying= YES;
#implementation AsyncCopyController
static void statusCallback (FSFileOperationRef fileOp,
const FSRef *currentItem,
FSFileOperationStage stage,
OSStatus error,
CFDictionaryRef statusDictionary,
void *info )
{
NSLog(#"Callback got called. %ld", error);
id delegate;
if (info)
delegate = (id)info;
if (error!=0) {
if (error==-48) {
[delegate didReceiveCopyError:#"Duplicate filename and version or Destination file already exists or File found instead of folder"];
}
}
CFURLRef theURL = CFURLCreateFromFSRef( kCFAllocatorDefault, currentItem );
NSString* currentPath = [(NSURL *)theURL path];
// NSLog(#"currentPath %#", currentPath);
// If the status dictionary is valid, we can grab the current values to
// display status changes, or in our case to update the progress indicator.
if (statusDictionary)
{
CFNumberRef bytesCompleted;
bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
kFSOperationBytesCompleteKey);
CGFloat floatBytesCompleted;
CFNumberGetValue (bytesCompleted, kCFNumberMaxType,
&floatBytesCompleted);
// NSLog(#"Copied %d bytes so far.",
// (unsigned long long)floatBytesCompleted);
if (info)
[delegate didReceiveCurrentPath :currentPath bytesCompleted :floatBytesCompleted currentStageOfFileOperation:stage];
}
NSLog(#"stage %d", stage);
if (stage == kFSOperationStageComplete) {
NSLog(#"Finished copying the file");
if (info)
[delegate didCopyOperationComplete:YES];
// Would like to call a Cocoa Method here...
}
if (!copying) {
FSFileOperationCancel(fileOp);
}
}
-(void)cancelAllAsyncCopyOperation
{
copying = NO;
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object
{
NSLog(#"copySource");
copying = YES;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSLog(#"%#", runLoop);
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
require(fileOp, FSFileOperationCreateFailed);
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp,
runLoop, kCFRunLoopDefaultMode);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
return status;
}
require_noerr(status, FSFileOperationScheduleWithRunLoopFailed);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
//return NO;
}
// Create a filesystem ref structure for the source and destination and
// populate them with their respective paths from our NSTextFields.
FSRef source;
FSRef destination;
// Used FSPathMakeRefWithOptions instead of FSPathMakeRef
// because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference
status = FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&source,
NULL);
require_noerr(status, FSPathMakeRefWithOptionsaSourceFailed);
Boolean isDir = true;
status = FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&destination,
&isDir);
require_noerr(status, FSPathMakeRefWithOptionsaDestDirFailed);
// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)
FSFileOperationClientContext clientContext;
// The FSFileOperation will copy the data from the passed in clientContext so using
// a stack based record that goes out of scope during the operation is fine.
if (object)
{
clientContext.version = 0;
clientContext.info = (void *) object;
clientContext.retain = CFRetain;
clientContext.release = CFRelease;
clientContext.copyDescription = CFCopyDescription;
}
// Start the async copy.
status = FSCopyObjectAsync (fileOp,
&source,
&destination, // Full path to destination dir
NULL,// Use the same filename as source
kFSFileOperationDefaultOptions,
statusCallback,
1.0,
object != NULL ? &clientContext : NULL);
//CFRelease(fileOp);
NSLog(#"Failed to begin asynchronous object copy: %d", status);
if (status) {
NSString * errMsg = [NSString stringWithFormat:#" - %#", status];
NSLog(#"Failed to begin asynchronous object copy: %d", status);
}
if (object)
{
[object release];
}
FSFileOperationScheduleWithRunLoopFailed:
CFRelease(fileOp);
FSPathMakeRefWithOptionsaSourceFailed:
FSPathMakeRefWithOptionsaDestDirFailed:
FSFileOperationCreateFailed:
return status;
}
#end
FSCopyObjectAsync is Deprecated in OS X v10.8
copyfile(3) is alternative for FSCopyObjectAsync. Here is example of copyfile(3) with Progress Callback.

Lua and Objective C not running script

I am trying to create an objective c interface that encapsulates the functionality of storing and running a lua script (compiled or not.) My code for the script interface is as follows:
#import <Cocoa/Cocoa.h>
#import "Types.h"
#import "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#interface Script : NSObject<NSCoding> {
#public
s32 size;
s8* data;
BOOL done;
}
#property s32 size;
#property s8* data;
#property BOOL done;
- (id) initWithScript: (u8*)data andSize:(s32)size;
- (id) initFromFile: (const char*)file;
- (void) runWithState: (lua_State*)state;
- (void) encodeWithCoder: (NSCoder*)coder;
- (id) initWithCoder: (NSCoder*)coder;
#end
#import "Script.h"
#implementation Script
#synthesize size;
#synthesize data;
#synthesize done;
- (id) initWithScript: (s8*)d andSize:(s32)s
{
self = [super init];
self->size = s;
self->data = d;
return self;
}
- (id) initFromFile:(const char *)file
{
FILE* p;
p = fopen(file, "rb");
if(p == NULL) return [super init];
fseek(p, 0, SEEK_END);
s32 fs = ftell(p);
rewind(p);
u8* buffer = (u8*)malloc(fs);
fread(buffer, 1, fs, p);
fclose(p);
return [self initWithScript:buffer andSize:size];
}
- (void) runWithState: (lua_State*)state
{
if(luaL_loadbuffer(state, [self data], [self size], "Script") != 0)
{
NSLog(#"Error loading lua chunk.");
return;
}
lua_pcall(state, 0, LUA_MULTRET, 0);
}
- (void) encodeWithCoder: (NSCoder*)coder
{
[coder encodeInt: size forKey: #"Script.size"];
[coder encodeBytes:data length:size forKey:#"Script.data"];
}
- (id) initWithCoder: (NSCoder*)coder
{
self = [super init];
NSUInteger actualSize;
size = [coder decodeIntForKey: #"Script.size"];
data = [[coder decodeBytesForKey:#"Script.data" returnedLength:&actualSize] retain];
return self;
}
#end
Here is the main method:
#import "Script.h"
int main(int argc, char* argv[])
{
Script* script = [[Script alloc] initFromFile:"./test.lua"];
lua_State* state = luaL_newstate();
luaL_openlibs(state);
luaL_dostring(state, "print(_VERSION)");
[script runWithState:state];
luaL_dostring(state, "print(_VERSION)");
lua_close(state);
}
And the lua script is just:
print("O Hai World!")
Loading the file is correct, but I think it messes up at pcall.
Any Help is greatly appreciated.
Heading
Your code is so complex that there are no obvious faults. However, your next step should be to check the return value from lua_pcall. If it is nonzero, there will be an error message on the top of the stack which you can print via
fprintf(stderr, "Pcall failed: %s\n", lua_tostring(state, -1));
If you don't get a useful error message, my next step would be to dump the Lua stack. How many elements are on it? What is the (Lua) type of each element? What is the value. Functions lua_top, and luaL_typename will be useful. To print the value you will have to switch on the results of lua_type. Good luck.
I didn't run your code. But at first glance, I found that the signature of initWithScript: are different between header file (using u8*) and source file (using s8*).

Problem with creating sockets using CFSocket in Objective-C (iPhone app)

Ok, I have a problem with building a socket using Objective-C. If you'll take a look at my code below, through help with example code and other sources, I was able to build a complete socket I believe. The problem is that when I compile it, it builds fine (no syntax problems), but there are no sockets being created. As you'll notice I've commented out a lot of things in Server2.m and have isolated the problem to the very beginning when I create the struct for the listeningSocket. By the way, if this helps, it is part of the the server side of server-client application. Does anyone know why I would be getting this problem? Everything seemed to be working fine yesterday, and this morning I thought I would take a different approach to building the sockets, so I tried this. Thanks for any help!
Server_TrialViewController.m
#include <CFNetwork/CFSocketStream.h>
#import <UIKit/UIKit.h>
#import "Server2.h"
#import "Client_Test.h"
#interface Server_TrialViewController : UIViewController {
IBOutlet UIButton *ServerButton;
IBOutlet UIButton *ClientButton;
IBOutlet UILabel *statusLabel;
Server2 *server;
Client_Test *client;
}
#property(nonatomic, retain) UILabel *statusLabel;
#property(nonatomic, retain) Server2 *server;
#property(nonatomic, retain) Client_Test *client;
-(IBAction)serverButtonPressed;
-(IBAction)clientButtonPressed;
//-(void)sendMessageWithServer:(Server_Test *)SERVER AndClient:(Client_Test *)CLIENT;
#end
Server_TrialViewController.h
#import "Server_TrialViewController.h"
#implementation Server_TrialViewController
#synthesize statusLabel;
#synthesize server;
#synthesize client;
-(IBAction)serverButtonPressed {
if ([server start]) {
[statusLabel setText:#"Success"];
}
else {
if (server.status == NULL) {
[statusLabel setText: #"No Server: No statUpdate"];
}
else {
[statusLabel setText: #"No Server: Found statUpdate"];
}
}
}
-(IBAction)clientButtonPressed {
if ([client start]) {
[statusLabel setText:#"Client Started"];
}
else {
[statusLabel setText:#"Client Not Started"];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[super dealloc];
}
#end
Server2.h
#import <Foundation/Foundation.h>
#import "Server2Delegate.h"
#interface Server2 : NSObject
{
uint16_t port;
CFSocketRef listeningSocket;
id<Server2Delegate> delegate;
NSNetService* netService;
NSString *status;
}
// Initialize connection
- (BOOL)start;
- (void)stop;
// Delegate receives various notifications about the state of our server
#property(nonatomic,retain) id<Server2Delegate> delegate;
#property(nonatomic, retain) NSString *status;
#end
Server2.m
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <CFNetwork/CFSocketStream.h>
#import "Server2.h"
#import "Connection2.h"
#import "AppConfig2.h"
// Declare some private properties and methods
#interface Server2 ()
#property(nonatomic,assign) uint16_t port;
#property(nonatomic,retain) NSNetService* netService;
-(BOOL)createServer;
-(void)terminateServer;
#end
// Implementation of the Server interface
#implementation Server2
#synthesize delegate;
#synthesize port, netService;
#synthesize status;
// Cleanup
- (void)dealloc
{
self.netService = nil;
self.delegate = nil;
[super dealloc];
}
// Create server and announce it
- (BOOL)start
{
// Start the socket server
if ( ! [self createServer] )
{
status = #"Server Not Created";
return FALSE;
}
status = #"Server Created";
return TRUE;
}
// Close everything
- (void)stop {
[self terminateServer];
}
#pragma mark Callbacks
// Handle new connections
- (void)handleNewNativeSocket:(CFSocketNativeHandle)nativeSocketHandle {
Connection2* connection = [[[Connection2 alloc] initWithNativeSocketHandle:nativeSocketHandle] autorelease];
// In case of errors, close native socket handle
if ( connection == nil ) {
close(nativeSocketHandle);
return;
}
// finish connecting
if ( ! [connection connect] ) {
//status = #"Connection Not Made";
[connection close];
return;
}
//status = #"Connection Made";
// Pass this on to our delegate
[delegate handleNewConnection:connection];
}
// This function will be used as a callback while creating our listening socket via 'CFSocketCreate'
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
Server2 *server = (Server2*)info;
// We can only process "connection accepted" calls here
if ( type != kCFSocketAcceptCallBack ) {
return;
}
// for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle*)data;
[server handleNewNativeSocket:nativeSocketHandle];
}
#pragma mark Sockets and streams
- (BOOL)createServer
{
//// PART 1: Create a socket that can accept connections
// Socket context
// struct CFSocketContext {
// CFIndex version;
// void *info;
// CFAllocatorRetainCallBack retain;
// CFAllocatorReleaseCallBack release;
// CFAllocatorCopyDescriptionCallBack copyDescription;
// };
CFSocketContext socketContext = {0, self, NULL, NULL, NULL};
listeningSocket = CFSocketCreate(
kCFAllocatorDefault,
PF_INET, // The protocol family for the socket
SOCK_DGRAM, // The socket type to create
IPPROTO_UDP, // The protocol for the socket. TCP vs UDP.
0, //kCFSocketAcceptCallBack, // New connections will be automatically accepted and the callback is called with the data argument being a pointer to a CFSocketNativeHandle of the child socket.
NULL, //(CFSocketCallBack)&serverAcceptCallback,
&socketContext );
// Previous call might have failed
if ( listeningSocket == NULL ) {
status = #"listeningSocket Not Created";
return FALSE;
}
else {
status = #"listeningSocket Created";
return TRUE;
}
}
/*
// getsockopt will return existing socket option value via this variable
int existingValue = 1;
// Make sure that same listening socket address gets reused after every connection
setsockopt( CFSocketGetNative(listeningSocket),
SOL_SOCKET, SO_REUSEADDR, (void *)&existingValue,
sizeof(existingValue));
//// PART 2: Bind our socket to an endpoint.
// We will be listening on all available interfaces/addresses.
// Port will be assigned automatically by kernel.
struct sockaddr_in socketAddress;
memset(&socketAddress, 0, sizeof(socketAddress));
socketAddress.sin_len = sizeof(socketAddress);
socketAddress.sin_family = AF_INET; // Address family (IPv4 vs IPv6)
socketAddress.sin_port = 0; // Actual port will get assigned automatically by kernel
socketAddress.sin_addr.s_addr = htonl(INADDR_ANY); // We must use "network byte order" format (big-endian) for the value here
// Convert the endpoint data structure into something that CFSocket can use
NSData *socketAddressData =
[NSData dataWithBytes:&socketAddress length:sizeof(socketAddress)];
// Bind our socket to the endpoint. Check if successful.
if ( CFSocketSetAddress(listeningSocket, (CFDataRef)socketAddressData) != kCFSocketSuccess ) {
// Cleanup
if ( listeningSocket != NULL ) {
status = #"Socket Not Binded";
CFRelease(listeningSocket);
listeningSocket = NULL;
}
return FALSE;
}
status = #"Socket Binded";
//// PART 3: Find out what port kernel assigned to our socket
// We need it to advertise our service via Bonjour
NSData *socketAddressActualData = [(NSData *)CFSocketCopyAddress(listeningSocket) autorelease];
// Convert socket data into a usable structure
struct sockaddr_in socketAddressActual;
memcpy(&socketAddressActual, [socketAddressActualData bytes],
[socketAddressActualData length]);
self.port = ntohs(socketAddressActual.sin_port);
//// PART 4: Hook up our socket to the current run loop
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket, 0);
CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopCommonModes);
CFRelease(runLoopSource);
return TRUE;
}
*/
- (void) terminateServer {
if ( listeningSocket != nil ) {
CFSocketInvalidate(listeningSocket);
CFRelease(listeningSocket);
listeningSocket = nil;
}
}
#pragma mark -
#pragma mark NSNetService Delegate Method Implementations
// Delegate method, called by NSNetService in case service publishing fails for whatever reason
- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict {
if ( sender != self.netService ) {
return;
}
// Stop socket server
[self terminateServer];
}
#end
For people looking for information about CFSocket server here's the answer: The code above is working fine if you change "SOCK_DGRAM" to "SOCK_STREAM".
Have you tried setting kCFSocketAcceptCallBack to something other than 0?
If you're interested in socket programming on Mac OS X or the iPhone, I suggest you look at this example from Apple's documentation.