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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With