How to send a string in a UDP socket in iOS7? - ios

I am trying to send a simple string over UDP in my iOS7 app to a known IP and could not find a simple explanation and sample code on how to do that.
There is plenty out there about TCP but not so much about UDP and it has to be UDP in my case.

You could use https://github.com/robbiehanson/CocoaAsyncSocket, which is an Objective-C wrapper for TCP and UDP connections. It also contains sample code for TCP and UPD clients and servers.

You could use this wrapper-less simple gist.
UDPEchoClient.h
#import <Foundation/Foundation.h>
#interface UDPEchoClient : NSObject
- (BOOL) sendData:(const char *)msg;
#end
UDPEchoClient.m
#import "UDPEchoClient.h"
//
// CFSocket imports
//
#import <CoreFoundation/CoreFoundation.h>
#import <sys/socket.h>
#import <arpa/inet.h>
#import <netinet/in.h>
#define IP "host ip"
#define PORT host_port
static void dataAvailableCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
//
// receiving information sent back from the echo server
//
CFDataRef dataRef = (CFDataRef)data;
NSLog(#"data recieved (%s) ", CFDataGetBytePtr(dataRef));
}
#implementation UDPEchoClient
{
//
// socket for communication
//
CFSocketRef cfsocketout;
//
// address object to store the host details
//
struct sockaddr_in addr;
}
- (instancetype)init
{
self = [super init];
if (self) {
//
// instantiating the CFSocketRef
//
cfsocketout = CFSocketCreate(kCFAllocatorDefault,
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
kCFSocketDataCallBack,
dataAvailableCallback,
NULL);
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr(IP);
//
// set runloop for data reciever
//
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfsocketout, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
CFRelease(rls);
}
return self;
}
//
// returns true upon successfull sending
//
- (BOOL) sendData:(const char *)msg
{
//
// checking, is my socket is valid
//
if(cfsocketout)
{
//
// making the data from the address
//
CFDataRef addr_data = CFDataCreate(NULL, (const UInt8*)&addr, sizeof(addr));
//
// making the data from the message
//
CFDataRef msg_data = CFDataCreate(NULL, (const UInt8*)msg, strlen(msg));
//
// actually sending the data & catch the status
//
CFSocketError socketErr = CFSocketSendData(cfsocketout,
addr_data,
msg_data,
0);
//
// return true/false upon return value of the send function
//
return (socketErr == kCFSocketSuccess);
}
else
{
NSLog(#"socket reference is null");
return false;
}
}

Related

Implementing a simple SuperpoweredAdvancedAudioPlayer in swift

I am trying to implement a simple SuperpoweredAdvancedAudioPlayer in swift. I successfully modified the SuperpoweredCrossExample project so that playerA plays the song on starting the application.
ViewController.mm now looks like this:
#import "ViewController.h"
#import "SuperpoweredAdvancedAudioPlayer.h"
#import "SuperpoweredFilter.h"
#import "SuperpoweredRoll.h"
#import "SuperpoweredFlanger.h"
#import "SuperpoweredIOSAudioIO.h"
#import "SuperpoweredSimple.h"
#import <stdlib.h>
#define HEADROOM_DECIBEL 3.0f
static const float headroom = powf(10.0f, -HEADROOM_DECIBEL * 0.025);
/*
This is a .mm file, meaning it's Objective-C++.
You can perfectly mix it with Objective-C or Swift, until you keep the member variables and C++ related includes here.
Yes, the header file (.h) isn't the only place for member variables.
*/
#implementation ViewController {
SuperpoweredAdvancedAudioPlayer *playerA;
SuperpoweredIOSAudioIO *output;
float *stereoBuffer, volA;
unsigned int lastSamplerate;
}
void playerEventCallbackA(void *clientData, SuperpoweredAdvancedAudioPlayerEvent event, void *value) {
if (event == SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess) {
ViewController *self = (__bridge ViewController *)clientData;
self->playerA->setBpm(126.0f);
self->playerA->setFirstBeatMs(353);
self->playerA->setPosition(self->playerA->firstBeatMs, false, false);
};
}
// This is where the Superpowered magic happens.
static bool audioProcessing(void *clientdata, float **buffers, unsigned int inputChannels, unsigned int outputChannels, unsigned int numberOfSamples, unsigned int samplerate, uint64_t hostTime) {
__unsafe_unretained ViewController *self = (__bridge ViewController *)clientdata;
if (samplerate != self->lastSamplerate) { // Has samplerate changed?
self->lastSamplerate = samplerate;
self->playerA->setSamplerate(samplerate);
};
bool silence = !self->playerA->process(self->stereoBuffer, false, numberOfSamples, self->volA);
if (!silence) SuperpoweredDeInterleave(self->stereoBuffer, buffers[0], buffers[1], numberOfSamples); // The stereoBuffer is ready now, let's put the finished audio into the requested buffers.
return !silence;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self f];
}
- (void) f {
volA = 1.0f * headroom;
if (posix_memalign((void **)&stereoBuffer, 16, 4096 + 128) != 0) abort(); // Allocating memory, aligned to 16.
playerA = new SuperpoweredAdvancedAudioPlayer((__bridge void *)self, playerEventCallbackA, 44100, 0);
playerA->open([[[NSBundle mainBundle] pathForResource:#"lycka" ofType:#"mp3"] fileSystemRepresentation]);
output = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)self preferredBufferSize:12 preferredMinimumSamplerate:44100 audioSessionCategory:AVAudioSessionCategoryPlayback channels:2 audioProcessingCallback:audioProcessing clientdata:(__bridge void *)self];
[output start];
playerA->play(false);
}
- (void)dealloc {
delete playerA;
free(stereoBuffer);
#if !__has_feature(objc_arc)
[output release];
[super dealloc];
#endif
}
- (void)interruptionStarted {}
- (void)recordPermissionRefused {}
- (void)mapChannels:(multiOutputChannelMap *)outputMap inputMap:(multiInputChannelMap *)inputMap externalAudioDeviceName:(NSString *)externalAudioDeviceName outputsAndInputs:(NSString *)outputsAndInputs {}
- (void)interruptionEnded { // If a player plays Apple Lossless audio files, then we need this. Otherwise unnecessary.
playerA->onMediaserverInterrupt();
}
#end
I am trying to use the same code in swift following the same method used in SuperpoweredFrequencies project to import c++ files in swift.
Superpowered.h:
#import <UIKit/UIKit.h>
#interface Superpowered: NSObject
-(void) f;
#end
Superpowered.mm:
#import "Superpowered.h"
#import "Superpowered/Headers/SuperpoweredAdvancedAudioPlayer.h"
#import "Superpowered/Headers/SuperpoweredFilter.h"
#import "Superpowered/Headers/SuperpoweredRoll.h"
#import "Superpowered/Headers/SuperpoweredFlanger.h"
#import "Superpowered/SuperpoweredIOSAudioIO.h"
#import "Superpowered/Headers/SuperpoweredSimple.h"
#import <stdlib.h>
#define HEADROOM_DECIBEL 3.0f
static const float headroom = powf(10.0f, -HEADROOM_DECIBEL * 0.025);
/*
This is a .mm file, meaning it's Objective-C++.
You can perfectly mix it with Objective-C or Swift, until you keep the member variables and C++ related includes here.
Yes, the header file (.h) isn't the only place for member variables.
*/
#implementation Superpowered {
SuperpoweredAdvancedAudioPlayer *playerA;
SuperpoweredIOSAudioIO *output;
float *stereoBuffer, volA;
unsigned int lastSamplerate;
}
void playerEventCallbackA(void *clientData, SuperpoweredAdvancedAudioPlayerEvent event, void *value) {
if (event == SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess) {
Superpowered *self = (__bridge Superpowered *)clientData;
self->playerA->setBpm(126.0f);
self->playerA->setFirstBeatMs(353);
self->playerA->setPosition(self->playerA->firstBeatMs, false, false);
};
}
// This is where the Superpowered magic happens.
static bool audioProcessing(void *clientdata, float **buffers, unsigned int inputChannels, unsigned int outputChannels, unsigned int numberOfSamples, unsigned int samplerate, uint64_t hostTime) {
__unsafe_unretained Superpowered *self = (__bridge Superpowered *)clientdata;
if (samplerate != self->lastSamplerate) { // Has samplerate changed?
self->lastSamplerate = samplerate;
self->playerA->setSamplerate(samplerate);
};
bool silence = !self->playerA->process(self->stereoBuffer, false, numberOfSamples, self->volA);
if (!silence) SuperpoweredDeInterleave(self->stereoBuffer, buffers[0], buffers[1], numberOfSamples); // The stereoBuffer is ready now, let's put the finished audio into the requested buffers.
return !silence;
}
- (void)f {
volA = 1.0f * headroom;
if (posix_memalign((void **)&stereoBuffer, 16, 4096 + 128) != 0) abort(); // Allocating memory, aligned to 16.
playerA = new SuperpoweredAdvancedAudioPlayer((__bridge void *)self, playerEventCallbackA, 44100, 0);
playerA->open([[[NSBundle mainBundle] pathForResource:#"lycka" ofType:#"mp3"] fileSystemRepresentation]);
output = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)self preferredBufferSize:12 preferredMinimumSamplerate:44100 audioSessionCategory:AVAudioSessionCategoryPlayback channels:2 audioProcessingCallback:audioProcessing clientdata:(__bridge void *)self];
[output start];
playerA->play(false);
}
- (void)dealloc {
delete playerA;
free(stereoBuffer);
#if !__has_feature(objc_arc)
[output release];
[super dealloc];
#endif
}
- (void)interruptionStarted {}
- (void)recordPermissionRefused {}
- (void)mapChannels:(multiOutputChannelMap *)outputMap inputMap:(multiInputChannelMap *)inputMap externalAudioDeviceName:(NSString *)externalAudioDeviceName outputsAndInputs:(NSString *)outputsAndInputs {}
- (void)interruptionEnded { // If a player plays Apple Lossless audio files, then we need this. Otherwise unnecessary.
playerA->onMediaserverInterrupt();
}
#end
Project-Bridging-Header.h:
#import "Superpowered.h"
Controller.swift:
override func viewDidLoad() {
super.viewDidLoad()
let s = Superpowered();
s.f();
}
When running the app it crashes and gives the following error:
let s = Superpowered(); should be declared outside viewDidLoad(). Declaring it as an instance variable solved the problem.

CFSocket accept callback is not being called. What have I missed?

I'm currently writing a simple TCP server class in Objective-C which should be able to bind to and listen on a particular port. I particular I have a function -(void) start which creates a CFSockets, binds it to the port specified and adds it to a run loop. It looks like this:
- (void) start {
//This function is called after the initialiser
if([self isStarted]) //if already started, return without doing anything
return;
//create a socket
CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL};
socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)&SocketAcceptCallback, &socketCtxt);
NSAssert(socket, #"Error: Cannot create a socket...");
int yes = 1;
setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
//bind it to the desired port
struct sockaddr_in serverAddress;
socklen_t nameLen = 0;
nameLen = sizeof(serverAddress);
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_len = nameLen;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(self.port);
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
NSData* address4 = [NSData dataWithBytes:&serverAddress length:nameLen];
if(kCFSocketSuccess != CFSocketSetAddress(socket, (CFDataRef)address4)) {
if (socket) CFRelease(socket);
socket = NULL;
NSAssert(NO, #"Unable to bind the created socket to the desired port");
}
//add to the current run loop
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
assert(rls!=0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
//done!
}
I also define the callback function SocketAcceptCallback like this:
static void SocketAcceptCallback(CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
//this method is called after a connection is accepted
//here we bind the received connection to a pair of
//NSStreams
if(type == kCFSocketAcceptCallBack){
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
struct sockaddr_in peerAddress;
socklen_t peerLen = sizeof(peerAddress);
NSString * peer = nil;
if (getpeername(nativeSocketHandle, (struct sockaddr *)&peerAddress, (socklen_t *)&peerLen) == 0) {
peer = [NSString stringWithUTF8String:inet_ntoa(peerAddress.sin_addr)];
} else {
peer = #"Generic Peer";
}
NSLog(#"%# has connected", peer);
//Find corresponding SocketServer object -- it is passed to the callback in the INFO pointer
SocketServer *obj = (__bridge SocketServer*)info;
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocket(NULL, nativeSocketHandle, &readStream, &writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
//bridge to NSStreams
obj.inputStream = (NSInputStream *)readStream;
obj.outputStream = (NSOutputStream *)writeStream;
//set delegates
[obj.inputStream setDelegate:obj];
[obj.outputStream setDelegate:obj];
//schedule the runloops
[obj.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[obj.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//open connections
[obj.inputStream open];
[obj.outputStream open];
} else {
// on any failure, need to destroy the CFSocketNativeHandle
// since we are not going to use it any more
close(nativeSocketHandle);
}
/*
if (readStream) CFRelease(readStream);
if (writeStream) CFRelease(writeStream); */
}
}
In the callback, I bridge the connection to a pair of NSStreams which I have as member fields in my class and which I use to read and write.
Now the problem: I have tried to run this server and connect to it via telnet. It connects, but the aforementioned callback is not being called (I have checked by adding a diagnostic NSLog in it). I suspect I haven't added the socket to a run loop correctly, but I can't find any mistakes myself. Any ideas as to what it might be?

FFMPEG: closing RTSP stream cleanly -- av_read_frame crash on avformat_close_input

I'm using KxMovie: https://github.com/kolyvan/kxmovie
It appears to stop a stream and close the view controller one should use [pause];
However, I'm trying to receive a stream from a version of gstreamer that has a memory leak if a stream isn't closed properly (it's just left hanging).
So, just [pause]ing isn't an option for me.
I'm trying to use [closeFile] in the KxMovie decoder:
-(void) closeFile
{
[self closeAudioStream];
[self closeVideoStream];
[self closeSubtitleStream];
_videoStreams = nil;
_audioStreams = nil;
_subtitleStreams = nil;
if (_formatCtx) {
_formatCtx->interrupt_callback.opaque = NULL;
_formatCtx->interrupt_callback.callback = NULL;
avformat_close_input(&_formatCtx);
_formatCtx = NULL;
}
}
However, I usually get a EXC_BAD_ACCESS from av_read_frame after [closeFile] issues avformat_close_input.
Can anyone give me some advice on how to cleanly shutdown an RTSP stream using ffmpeg?
Thanks!
I was also confused by this, and I do not quite understand your solution.
I fixed it like below, could you give some advice?
_dispatchQueue is the same queue as doing asyncDecodeFrames work.
- (void)unSetup {
_buffered = NO;
_interrupted = YES;
dispatch_async(_dispatchQueue, ^{
if (_decoder) {
[self pause];
[self freeBufferedFrames];
if (_moviePosition == 0 || _decoder.isEOF)
[gHistory removeObjectForKey:_decoder.path];
else if (!_decoder.isNetwork)
[gHistory setValue:[NSNumber numberWithFloat:_moviePosition]
forKey:_decoder.path];
[_decoder closeFile];
}
});
}
Needed to use the interrupt callbacks to interrupt av_read_frame
_formatCtx->interrupt_callback.opaque
_formatCtx->interrupt_callback.callback
Wait for the callback to be called and return non zero.
After the callback has returned an interrupt value av_close_input can safely be called (after closing any codecs used).
The below code snippets are in Objective-C and the implementation file .m is for the object that handles RTSP stuff (RTSPProvider).
It is tested with Xcode Version 10.1 (10B61) and an FFmpeg manually built version of the current FFmpeg versions to date (4.2.1 / 15.10.2019).
Should you need the build script configuration and or library versions used (just ask).
I had the same issue as the OP but couldn't use his solution.
The full versions was with the interrupt callback I used was:
int interruptCallBack(void *ctx){
RTSPProviderObject *whyFFmpeg = (__bridge RTSPProviderObject*)ctx;
NSLog(#"What is this!");
if(whyFFmpeg.whatIsHappeningSTR) {
return 1;
} else {
return 0;
}
}
The return value 1 should have interrupted the av_read_frame() and exited without a crash as to my current understanding.
It still crashed. My solution was to let av_read_frame() finish reading and terminate the session context which will be freed and don't allow any more reading. This was easy since I had this issue when I deallocated my RTSPProviderObject and no reading was done.
The final usage was:
[self.rtspProvider cleanup];
self.rtspProvider = nil;
Below is the full code snippet:
#import "Don't forget the required ffmpeg headers or header file"
int interruptCallBack(void *ctx){
RTSPProviderObject *whyFFmpeg = (__bridge RTSPProviderObject*)ctx;
NSLog(#"What is this!");
if(whyFFmpeg.whatIsHappeningSTR) {
return 1;
} else {
return 0;
}
}
#interface RTSPProviderObject ()
#property (nonatomic, assign) AVFormatContext *sessionContext;
#property (nonatomic, assign) NSString *whatIsHappeningSTR;
#property (nonatomic, assign) AVDictionary *sessionOptions;
#property (nonatomic, assign) BOOL usesTcp;
#property (nonatomic, assign) BOOL isInputStreamOpen;
#property (nonatomic, strong) NSLock *audioPacketQueueLock;
#property (nonatomic, strong) NSLock *packetQueueLock;
#property (nonatomic, strong, readwrite) NSMutableArray *audioPacketQueue;
#property (nonatomic, assign) int selectedVideoStreamIndex;
#property (nonatomic, assign) int selectedAudioStreamIndex;
#end
#implementation RTSPProviderObject
- (id _Nullable)init
{
self = [super init];
if (!self)
{
return nil;
}
self.sessionContext = NULL;
self.sessionContext = avformat_alloc_context();
AVFormatContext *pFormatCtx = self.sessionContext;
if (!pFormatCtx)
{
// Error handling code...
}
// MUST be called before avformat_open_input().
av_dict_free(&_sessionOptions);
self.sessionOptions = 0;
if (self.usesTcp)
{
// "rtsp_transport" - Set RTSP transport protocols.
// Allowed are: udp_multicast, tcp, udp, http.
av_dict_set(&_sessionOptions, "rtsp_transport", "tcp", 0);
}
// Open an input stream and read the header with the demuxer options.
// rtspURL - connection url to your remote ip camera which supports RTSP 2.0.
if (avformat_open_input(&pFormatCtx, rtspURL.UTF8String, NULL, &_sessionOptions) != 0)
{
self.isInputStreamOpen = NO;
// Error handling code...
}
self.isInputStreamOpen = YES;
// user-supplied AVFormatContext pFormatCtx might have been modified.
self.sessionContext = pFormatCtx;
pFormatCtx->interrupt_callback.callback = interruptCallBack;
pFormatCtx->interrupt_callback.opaque = (__bridge void *)(self);
// ... Other needed but currently not relevant code for codec/stream and other setup.
}
- (BOOL)prepareNextFrame
{
NSLog(#"%s", __PRETTY_FUNCTION__);
int isVideoFrameAvailable = 0;
// The session context is needed to provide frame data. Frame data is provided for video and audio.
// av_read_frame reads from pFormatCtx.
AVFormatContext *pFormatCtx = self.sessionContext;
if (!pFormatCtx) { return NO; }
// Audio packet access is forbidden.
[self.packetQueueLock lock];
BOOL readResult = YES;
// Calling av_read_frame while it is reading causes a bad_exception.
// We read frames as long as the session context cotains frames to be read and cosumed (usually one).
while (!isVideoFrameAvailable && self.isInputStreamOpen && readResult) {
if (packet.buf == nil && self.whatIsHappeningSTR) {
[self.packetQueueLock unlock];
return NO;
}
NSLog(#"New frame will be read.");
if (self.shouldTerminateStreams) {
[self terminate];
[self.packetQueueLock unlock];
return NO;
}
readResult = av_read_frame(pFormatCtx, &packet) >=0;
// Video packet data decoding.
// We need to make sure that the frame video data which is consumed matches the user selected stream.
if(packet.stream_index == self.selectedVideoStreamId) {
// DEPRECIATED:
// avcodec_decode_video2(self.videoCodecContext, self.rawFrameData, &isVideoFrameAvailable, &packet);
// Replaced by this new implememtation. Read more: https://blogs.gentoo.org/lu_zero/2016/03/29/new-avcodec-api/
// *
// We need the video context to decode video data.
AVCodecContext *videoContext = self.videoCodecContext;
if (!videoContext && videoContext->codec_type == AVMEDIA_TYPE_VIDEO) { isVideoFrameAvailable = 1; }
int ret;
// Supply raw packet data as input to a decoder.
ret = avcodec_send_packet(videoContext, &packet);
if (ret < 0)
{
NSLog(#"codec: sending video packet failed");
[self.packetQueueLock unlock];
return NO;
}
// Return decoded output data from a decoder.
ret = avcodec_receive_frame(videoContext, self.rawFrameData);
if (isVideoFrameAvailable < 0 && isVideoFrameAvailable != AVERROR(EAGAIN) && isVideoFrameAvailable != AVERROR_EOF)
{
[self.packetQueueLock unlock];
return NO;
}
if (ret >= 0) { isVideoFrameAvailable = 1; }
// *
} else {
// avcodec_decode_video2 unreference all the buffers referenced by self.rawFrameData and reset the frame fields.
// We must do this manually if we don't use the video frame or we will leak the frame data.
av_frame_unref(self.rawFrameData);
isVideoFrameAvailable = 1;
}
// Audio packet data consumption.
// We need to make sure that the frame audio data which will be consumed matches the user selected stream.
if (packet.stream_index == self.selectedAudioStreamIndex) {
[self.audioPacketQueueLock lock];
[self.audioPacketQueue addObject:[NSMutableData dataWithBytes:&packet length:sizeof(packet)]];
[self.audioPacketQueueLock unlock];
}
}
[self.packetQueueLock unlock];
return isVideoFrameAvailable!=0;
}
- (void)cleanup
{
NSLog(#"%s", __PRETTY_FUNCTION__);
self.shouldTerminateStreams = YES;
self.whatIsHappeningSTR = #"";
}
- (void)terminate
{
avformat_close_input(&_sessionContext);
}
#end
Hope this helps anyone. Thank you for reading and contributing.

NSNetServiceBrowser does not find Service

I tried to write a Client(iPad)/Server(iMac) application based on the CocoaEcho example. My first simple example worked, but after adding more functionality the client is unable to find the server.
After starting the server, I start the client, both in a local network. The client starts searching for services and gets a "netServiceBrowserWillSearch:" message for its browser, but after that nothing happens. Triggering the search for services again, results in a "didNotsearch:" message with error -72003, 10 (browser is still busy searching).
1) I checked that the server is reachable with the WiTap app. There client and server connect correctly.
2) I checked if the server publishes the service with "dns-sd -B _cocoaecho", it is detected.
3) The nsnetservicebrowser object in the client app is declared a property, so there should not be a scope problem. I also checked in the debugger, it is still there....
My Code:
Client:
#interface MySocketClient : UIResponder <NSNetServiceBrowserDelegate, NSStreamDelegate>
{
...
NSNetService * myServer;
NSString* nextMsg;
}
#property (nonatomic, strong, readwrite) NSMutableArray * services; // of NSNetService
#property (nonatomic, strong, readwrite) NSNetServiceBrowser * serviceBrowser;
#property (nonatomic, strong, readwrite) NSInputStream * inputStream;
#property (nonatomic, strong, readwrite) NSOutputStream * outputStream;
#property (nonatomic, strong, readwrite) NSMutableData * inputBuffer;
#property (nonatomic, strong, readwrite) NSMutableData * outputBuffer;
....
-(void) setup{
...
self.serviceBrowser = [[NSNetServiceBrowser alloc] init];
self.services = [[NSMutableArray alloc] init];
[self.serviceBrowser setDelegate:self];
[self.serviceBrowser searchForServicesOfType:#"_cocoaecho._tcp." inDomain:#"local."];
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didNotSearch:(NSDictionary *)errorInfo
{
NSLog(#"%#", errorInfo);
}
// Sent when browsing begins
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
{
NSLog(#"will search \n");
}
// Sent when browsing stops
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
{
NSLog(#"stopped search \n");
}
//We broadcast the willChangeValueForKey: and didChangeValueForKey: for the NSTableView binding to work.
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
#pragma unused(aNetServiceBrowser)
#pragma unused(moreComing)
NSLog(#"found a service \n");
if (![self.services containsObject:aNetService]) {
[self willChangeValueForKey:#"services"];
[self.services addObject:aNetService];
[self didChangeValueForKey:#"services"];
myServer = aNetService;
}
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
#pragma unused(aNetServiceBrowser)
#pragma unused(moreComing)
if ([self.services containsObject:aNetService]) {
[self willChangeValueForKey:#"services"];
[self.services removeObject:aNetService];
[self didChangeValueForKey:#"services"];
}
}
And the Server:
- (BOOL)start {
assert(_ipv4socket == NULL && _ipv6socket == NULL); // don't call -start twice!
CFSocketContext socketCtxt = {0, (__bridge void *) self, NULL, NULL, NULL};
_ipv4socket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, &EchoServerAcceptCallBack, &socketCtxt);
_ipv6socket = CFSocketCreate(kCFAllocatorDefault, AF_INET6, SOCK_STREAM, 0, kCFSocketAcceptCallBack, &EchoServerAcceptCallBack, &socketCtxt);
if (NULL == _ipv4socket || NULL == _ipv6socket) {
[self stop];
return NO;
}
static const int yes = 1;
(void) setsockopt(CFSocketGetNative(_ipv4socket), SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
(void) setsockopt(CFSocketGetNative(_ipv6socket), SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
// Set up the IPv4 listening socket; port is 0, which will cause the kernel to choose a port for us.
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(0);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (kCFSocketSuccess != CFSocketSetAddress(_ipv4socket, (__bridge CFDataRef) [NSData dataWithBytes:&addr4 length:sizeof(addr4)])) {
[self stop];
return NO;
}
// Now that the IPv4 binding was successful, we get the port number
// -- we will need it for the IPv6 listening socket and for the NSNetService.
NSData *addr = (__bridge_transfer NSData *)CFSocketCopyAddress(_ipv4socket);
assert([addr length] == sizeof(struct sockaddr_in));
self.port = ntohs(((const struct sockaddr_in *)[addr bytes])->sin_port);
// Set up the IPv6 listening socket.
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(self.port);
memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
if (kCFSocketSuccess != CFSocketSetAddress(_ipv6socket, (__bridge CFDataRef) [NSData dataWithBytes:&addr6 length:sizeof(addr6)])) {
[self stop];
return NO;
}
// Set up the run loop sources for the sockets.
CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv4socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source4, kCFRunLoopCommonModes);
CFRelease(source4);
CFRunLoopSourceRef source6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv6socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source6, kCFRunLoopCommonModes);
CFRelease(source6);
assert(self.port > 0 && self.port < 65536);
self.netService = [[NSNetService alloc] initWithDomain:#"local." type:#"_cocoaecho._tcp." name:#"" port:(int) self.port];
[self.netService publishWithOptions:0];
return YES;
}
I was just getting that -72003 error all the time unless I disconnected and reconnected again (even the first time). Which lead to this solution:
private let serviceBrowser = NSNetServiceBrowser()
serviceBrowser.stop()
serviceBrowser.searchForServicesOfType(TYPE, inDomain: DOMAIN)
I don't know why this works but I'm no longer getting the error.
I had similar problem. My code successfully registered NSNetService and launched NSNetServiceBrowser but could not -resolveWithTimeout other devices. Strange, but sometimes did work, sometimes not and sometimes worked asymmetrically.
After intense debugging I can give you some tips to check:
Install Bonjour Browser on desktop. Plug out your network cable and check if you are connected to the same WiFi hotspot as mobile devices. Here you should see the same service state as mobile devices will do.
Try with different WiFi hotspot. Strange but my main WiFi performed badly. After I switched to another one it worked like a charm using the very same code. Try unplug WiFi from Internet for testing.
You can add some retains (or assign to static variable) to objects returned from API (like NSNetService). ARC can do silently dealloc if it decides object is not needed anymore. That helped my for some tests.

Stuck in loop form CFRunLoop

ViewController.h
#interface ViewController : UIViewController{
CFSocketRef s;
int connectFlag;
}
ViewController.m
void receiveDataCilent(CFSocketRef cs,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
CFDataRef df = (CFDataRef) data;
int len = CFDataGetLength(df);
if(len <= 0) return;
CFRange range = CFRangeMake(0,len);
UInt8 buffer[len];
NSLog(#"Received %d bytes from socket %d\n",
len, CFSocketGetNative(cs));
CFDataGetBytes(df, range, buffer);
NSLog(#"Client received: %s\n", buffer);
NSLog(#"As UInt8 coding: %#", df);
NSLog(#"len value: %d", len);
}
-(void) clientConnect:(int)sender;{
s = CFSocketCreate(NULL, PF_INET,
SOCK_STREAM, IPPROTO_TCP,
kCFSocketDataCallBack,
receiveDataCilent,
NULL);
struct sockaddr_in sin;
struct hostent *host;
memset(&sin, 0, sizeof(sin));
host = gethostbyname("localhost");
memcpy(&(sin.sin_addr), host->h_addr,host->h_length);
sin.sin_family = AF_INET;
sin.sin_port = htons(6666);
CFDataRef address;
CFRunLoopSourceRef source;
address = CFDataCreate(NULL, (UInt8 *)&sin, sizeof(sin));
CFSocketConnectToAddress(s, address, 0);
CFRelease(address);
source = CFSocketCreateRunLoopSource(NULL, s, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
source,
kCFRunLoopDefaultMode);
CFRelease(source);
CFRunLoopRun();
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(connectFlag == 0 ){
[self clientConnect:1];
}
}
First I'm sorry for messing the concept of xcode programming and threading programming I'm very new to xcode and I need to finish my game project soon
This code is suppose to connect to server and waiting for message from server. I try and test it and it work fine. In view I have IBAction that can press and change view.
But after I implement it to my main project. It can run but I cannot press any button or do anything. After log it out. It seem it stuck around CFRunLoopRun(); I think it stuck in loop but why when I test it. it not stuck ?
In my project this code is run from another view not from ViewController like I just test.
I don't know why it stuck in my project.
Any help would be appreciated.
Don't run the run loop yourself on the main thread. The main event loop will run it.
Edit: I should say, "within the context of a GUI app". In a command-line tool, you do need to run the run loop yourself.

Resources