Ran Analyzer : Potential Memory Leak - ios

Hi,
I ran the XCode analyser - and it tells me that the following both are potential memory leaks.
I'm not sure why.
I declared midiDevices like this
#property (assign, nonatomic) NSMutableArray* midiDevices;
Here's the code for openMidiIn
-(void)openMidiIn {
int k, endpoints;
CFStringRef name = NULL, cname = NULL, pname = NULL;
CFStringEncoding defaultEncoding = CFStringGetSystemEncoding();
MIDIPortRef mport = NULL;
MIDIEndpointRef endpoint;
OSStatus ret;
/* MIDI client */
cname = CFStringCreateWithCString(NULL, "my client", defaultEncoding);
ret = MIDIClientCreate(cname, NULL, NULL, &mclient);
if(!ret){
/* MIDI output port */
pname = CFStringCreateWithCString(NULL, "outport", defaultEncoding);
ret = MIDIInputPortCreate(mclient, pname, MidiWidgetsManagerReadProc, self, &mport);
if(!ret){
/* sources, we connect to all available input sources */
endpoints = MIDIGetNumberOfSources();
//NSLog(#"midi srcs %d\n", endpoints);
for(k=0; k < endpoints; k++){
endpoint = MIDIGetSource(k);
void *srcRefCon = endpoint;
MIDIPortConnectSource(mport, endpoint, srcRefCon);
}
}
}
if(name) CFRelease(name);
if(pname) CFRelease(pname);
if(cname) CFRelease(cname);
}
Thanks for your help.
analyzer info
Here's more info about the error about making a bit of changes.

Assuming you're using ARC, that object will actually be released and dealloc'd instantly. Why it's saying you have a memory leak is confusing, but you will have a dead reference. Use strong, not assign.

Related

iOS Resolve DNS programmatically is returning invalid IPs sometimes

Hi,
I am working on an iOS app which requires to resolve DNS programmatically.
Consider this as a proxy to resolve all dns queries on iPhone. I receive DNS queries from each app on iPhone and send back corresponding IPList
I have tried a couple of methods but both have same kind of responses. The one I decided to move with is given below resolveHost function written in objective-c and c I am calling this method from swift code.
This is how I am calling from swift, also sharing sample host/url, value of host can be any domain ("google.com, apple.com etc") or a domain/host as a result of trails when you open a site in mkwebview
let host = "www.opera.com"
let ipArray = ResolveUtil().resolveHost(host, usingDNSServer: "8.8.8.8") as! [String]
More specifically Facebook app does not work well with IPs returned from function resolveHost
By not working well I mean app does not connect to IPs returned from the functions
Some times it returns 192.16.192.16 as part of other IPs for some hosts/domains. What is this IP?
- (NSArray*)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer
{
NSMutableArray* result = [[NSMutableArray alloc] init];
struct __res_state res;
setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
int count;
char** ips = query_ips(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], &count);
for (int i=0; i<count; i++){
[result addObject:[[NSString alloc] initWithCString:ips[i] encoding:NSASCIIStringEncoding]];
}
for (int i=0; i<count; i++){
free(ips[i]);
}
free(ips);
ips = NULL;
return result;
}
char ** query_ips(res_state res, const char *host, int* count)
{
u_char answer[NS_PACKETSZ];
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int messageCount = ns_msg_count(handle, ns_s_an);
*count = messageCount;
char **ips = malloc(messageCount * sizeof(char *));
for (int i=0; i < messageCount; i++) {
ips[i] = malloc(16 * sizeof(char));
memset(ips[i], '\0', sizeof(16));
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
return ips;
}
Other Method
func resolveIp(_ hostUrl:String) -> [String]{
var ips:[String] = [String]()
let host = CFHostCreateWithName(nil,hostUrl as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
for case let theAddress as NSData in addresses {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
let numAddress = String(cString: hostname)
ips.append(numAddress)
}
}
}
Logger.info("\(#function) validIPs:\(ips.joined(separator: "-")) url:\(hostUrl)")
return ips
}
192.16.192.16
What is this IP? Perfectly valid IPv4. It resolves back to basento.nikhef.nl.
Why is it returned?
I don't know. Maybe, see resolv.h:
* Mac OS supports a DNS query routing API (see <dns.h>) which is used by
* most system services to access DNS. The BIND-9 APIs described here are
* a lower-level that does not do query routing or search amongst multiple
* resolver clients. The results of DNS queries from this API may differ
* significantly from the results of queries sent to the <dns.h> API. We
* strongly encourage developers to use higher-level APIs where possible.
By not working well I mean app does not connect to IPs returned from the functions.
This has nothing to do with the name/ip address resolution.
The problem can be elsewhere. Your provider can block it, no service is running on the IP address, you're not allowed to access it, ... Many reasons.
More specifically Facebook app does not work well with IPs returned from function resolveHost.
This doesn't make sense to me at all. You have your own app, you're resolving IP addresses in it and then saying that it doesn't work with Facebook. Frankly, I have no idea what do you mean with this.
Why am I answering this question? Well, you shouldn't blindly copy & paste code from other Stack Overflow questions or any other sites. Did a research and it looks like a copy & paste of some other answers.
Why? The code in your question doesn't handle errors, doesn't follow documentation, ... It's a pure luck that it works for you.
What if this is your problem? Did you ever consider this option?
Here's an example of Resolver you can use / test with your conditions. It may or may not fix your issues.
#import <resolv.h>
#import Darwin.POSIX.arpa;
#interface Resolver: NSObject
- (nullable instancetype)initWithDNSServer:(nonnull NSString *)server;
- (nullable NSArray<NSString *> *)resolveHost:(nonnull NSString *)host;
#end
#implementation Resolver {
struct __res_state *state;
}
- (void)dealloc {
if (state != NULL) {
// man 3 resolver:
//
// res_ndestroy() should be call to free memory allocated by res_ninit() after last use.
if ((state->options & RES_INIT) == RES_INIT) {
res_ndestroy(state);
}
free(state);
state = NULL;
}
}
- (nullable instancetype)initWithDNSServer:(nonnull NSString *)server {
if ((self = [super init]) == nil) {
return nil;
}
// man 3 resolver:
//
// The memory referred to by statp must be set to all zeros prior
// to the first call to res_ninit(). res_ndestroy() should be call to free memory
// allocated by res_ninit() after last use.
if ((state = calloc(1, sizeof(*state))) == NULL) {
return nil;
}
// 0 success
if (res_ninit(state) != 0) {
return nil;
}
// Avoid calling inet_aton later with NULL if we can't convert it to ASCII
if (![server canBeConvertedToEncoding:NSASCIIStringEncoding]) {
return nil;
}
struct in_addr addr;
// man 3 inet_aton:
//
// It returns 1 if the string was successfully interpreted ...
if (inet_aton([server cStringUsingEncoding:NSASCIIStringEncoding], &addr) != 1) {
return nil;
}
state->nsaddr_list[0].sin_addr = addr;
state->nsaddr_list[0].sin_family = AF_INET;
state->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
state->nscount = 1;
return self;
}
- (nullable NSArray<NSString *> *)resolveHost:(nonnull NSString *)host {
// Avoid calling res_nquery with NULL
if (![host canBeConvertedToEncoding:NSASCIIStringEncoding]) {
return nil;
}
u_char answer[NS_PACKETSZ];
int len = res_nquery(state, [host cStringUsingEncoding:NSASCIIStringEncoding],
ns_c_in, ns_t_a, answer, sizeof(answer));
// -1 = error
if (len == -1) {
return nil;
}
ns_msg handle;
// 0 success, -1 error
if (ns_initparse(answer, len, &handle) != 0) {
return nil;
}
u_int16_t count = ns_msg_count(handle, ns_s_an);
NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
for (int i = 0 ; i < count ; i++) {
ns_rr rr;
// 0 success, -1 error
if (ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
char *address = inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr));
if (address == NULL) {
continue;
}
NSString *ip = [NSString stringWithCString:address
encoding:NSASCIIStringEncoding];
[result addObject:ip];
}
}
return result;
}
#end
You can use it in this way:
Resolver *resolver = [[Resolver alloc] initWithDNSServer:#"8.8.8.8"];
NSLog(#"%#", [resolver resolveHost:#"www.opera.com"]);
Output:
(
"13.102.114.111",
"52.57.141.185",
"18.196.127.98"
)
I might have not explained the problem statement in my original question but I managed to fix the bug, so I thought I should write here my findings.
My app works as a dns proxy, so its main responsibility was to resolve domains and return IPs.
I used resolveHost function to resolve the IP. This function has all the issues mentioned by zrzka so if somebody wants to use please do consider his points.
The problem I had was that the function returns a few IPs against specific hosts/domains which does not seem valid, I am saying invalid because these were not pingable IPs and from Wireshark I confirmed connection on these IPs were unsuccessful, even if returned IPList contains valid IP at some index it was still causing unnecessary delay due to first try on invalid IPs as they reside before valid IPs in the list.
On further investigation I came to know these invalid IPs were against answer type CNAME which depicts Alias in DNS record, I don't know I should still call them invalid or not but ignoring them did the job for me. Now I only accept A type or AAAA type answers from DNS response. I have achieved this by a simple check in the following function.
char ** query_ips(res_state res, const char *host, int* count)
{
u_char answer[NS_PACKETSZ];
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int messageCount = ns_msg_count(handle, ns_s_an);
*count = messageCount;
char **ips = malloc(messageCount * sizeof(char *));
for (int i=0; i < messageCount; i++) {
ips[i] = malloc(16 * sizeof(char));
memset(ips[i], '\0', sizeof(16));
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
if (1 == rr.type || 28 == rr.type) // here is the new check
strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
return ips;
}

Assigning from a Class method binds the class instead of its return value

I've noticed something strange in the behavior of code I'm currently writing and thought I would ask here to see if I'm doing something silly that would cause this to happen.
Basically, when I assign a variable to the return value of my class method, instead of the variable holding a reference to the return value, it's holding a reference to the class. See the code below:
NSArray * newAddresses = [MyHost addressesForHostname: #"google.com"];
Which has a method signature of
+ (NSArray *) addressesForHostname: (NSString *)hostname
And returns
return (__bridge_transfer NSArray *) ipAddresses; // ipAddresses is a CFMutableArrayRef
As you can see, I'm using toll-free bridging to use CoreFoundation objects as I'm collecting a list of IP addresses for some network interfaces.
After newAddresses has been assigned to, I look at the class of the newAddresses array in LLDB and get:
(lldb) po [newAddresses class]
MyHost
Am I mistaken in my assumptions about how I'm using __bridge_transfer? All of the objects use to make up ipAddresses are CFStringRefs.
EDIT: I was asked for the whole method, so here it is!
+ (NSArray *) addressesForHostname: (NSString *)hostname {
CFMutableArrayRef ipAddresses;
DLog(#"Getting addresses for host name %#", hostname);
CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)(hostname));
CFStreamError error;
BOOL didResolve = CFHostStartInfoResolution(hostRef, kCFHostNames, &error); // synchronously get the host.
if (didResolve) {
CFArrayRef responseObjects = CFHostGetAddressing(hostRef, NULL);
long numberOfResponses = CFArrayGetCount(responseObjects);
ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault, numberOfResponses, &kCFTypeArrayCallBacks);
for ( int i = 0 ; i < numberOfResponses; ++i ) {
char * ipAddress;
CFDataRef responseObject = CFArrayGetValueAtIndex(responseObjects, i);
struct sockaddr * currentAddress = (struct sockaddr *) CFDataGetBytePtr(responseObject); // Unwrap the CFData wrapper aound the sockaddr struct
switch (currentAddress->sa_family) {
case AF_INET: { // Internetworking AKA IPV4
DLog(#"Extracting IPV4 address");
struct sockaddr_in * socketAddress = (struct sockaddr_in *) currentAddress;
ipAddress = malloc(sizeof(INET_ADDRSTRLEN));
inet_ntop(AF_INET,
&(socketAddress->sin_addr),
ipAddress,
INET_ADDRSTRLEN);
CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);
CFArrayInsertValueAtIndex(ipAddresses, i, ipAddressString);
free(ipAddress);
break;
}
case AF_INET6: { // IPV6
DLog(#"Extracting IPV6 address");
struct sockaddr_in6 * socketAddress = (struct sockaddr_in6 *) currentAddress;
ipAddress = malloc(sizeof(INET6_ADDRSTRLEN));
inet_ntop(AF_INET6,
&(socketAddress->sin6_addr),
ipAddress,
INET6_ADDRSTRLEN);
CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);
CFArrayInsertValueAtIndex(ipAddresses, i, ipAddressString);
free(ipAddress);
break;
}
default:
DLog(#"Unsupported addressing protocol encountered. Gracefully ignoring and continuing.");
break;
}
}
CFRelease(responseObjects);
}
CFRelease(hostRef);
return (__bridge_transfer NSArray *) ipAddresses;
}
So I found the solution, and it lies in me forgetting to initialize ipAddresses = nil before anything happens. The way this code is written, it won't assign a value to ipAddresses if it's unable to resolve the hostRef given to CFHostStartInfoResolution. With no value in ipAddresses, it returns an uninitialized pointer that gets casted and has its ownership transferred.
I can't find formal documentation that states this, but I believe this would be undefined behavior.
I should state that if anyone is using this code as reference, I'm experiencing inconsistent crashes on the line where I release hostRef. This is unrelated to the issue that I created for this thread, but is worthwhile to point out.

Memory leak in a Tcl wrapper

I read all I could find about memory management in the Tcl API, but haven't been able to solve my problem so far. I wrote a Tcl extension to access an existing application. It works, except for a serious issue: memory leak.
I tried to reproduce the problem with minimal code, which you can find at the end of the post. The extension defines a new command, recordings, in namespace vtcl. The recordings command creates a list of 10000 elements, each element being a new command. Each command has data attached to it, which is the name of a recording. The name subcommand of each command returns the name of the recording.
I run the following Tcl code with tclsh to reproduce the problem:
load libvtcl.so
for {set ii 0} {$ii < 1000} {incr ii} {
set recs [vtcl::recordings]
foreach r $recs {rename $r ""}
}
The line foreach r $recs {rename $r ""} deletes all the commands at each iteration, which frees the memory of the piece of data attached to each command (I can see that in gdb). I can also see in gdb that the reference count of variable recs goes to 0 at each iteration so that the contents of the list is freed. Nonetheless, I see the memory of the process running tclsh going up at each iteration.
I have no more idea what else I could try. Help will be greatly appreciated.
#include <stdio.h>
#include <string.h>
#include <tcl.h>
static void DecrementRefCount(ClientData cd);
static int ListRecordingsCmd(ClientData cd, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]);
static int RecordingCmd(ClientData cd, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]);
static void
DecrementRefCount(ClientData cd)
{
Tcl_Obj *obj = (Tcl_Obj *) cd;
Tcl_DecrRefCount(obj);
return;
}
static int
ListRecordingsCmd(ClientData cd, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[])
{
char name_buf[20];
Tcl_Obj *rec_list = Tcl_NewListObj(0, NULL);
for (int ii = 0; ii < 10000; ii++)
{
static int obj_id = 0;
Tcl_Obj *cmd;
Tcl_Obj *rec_name;
cmd = Tcl_NewStringObj ("rec", -1);
Tcl_AppendObjToObj (cmd, Tcl_NewIntObj (obj_id++));
rec_name = Tcl_NewStringObj ("DM", -1);
snprintf(name_buf, sizeof(name_buf), "%04d", ii);
Tcl_AppendStringsToObj(rec_name, name_buf, (char *) NULL);
Tcl_IncrRefCount(rec_name);
Tcl_CreateObjCommand (interp, Tcl_GetString (cmd), RecordingCmd,
(ClientData) rec_name, DecrementRefCount);
Tcl_ListObjAppendElement (interp, rec_list, cmd);
}
Tcl_SetObjResult (interp, rec_list);
return TCL_OK;
}
static int
RecordingCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
Tcl_Obj *rec_name = (Tcl_Obj *)cd;
char *subcmd;
subcmd = Tcl_GetString (objv[1]);
if (strcmp (subcmd, "name") == 0)
{
Tcl_SetObjResult (interp, rec_name);
}
else
{
Tcl_Obj *result = Tcl_NewStringObj ("", 0);
Tcl_AppendStringsToObj (result,
"bad command \"",
Tcl_GetString (objv[1]),
"\"",
(char *) NULL);
Tcl_SetObjResult (interp, result);
return TCL_ERROR;
}
return TCL_OK;
}
int
Vtcl_Init(Tcl_Interp *interp)
{
#ifdef USE_TCL_STUBS
if (Tcl_InitStubs(interp, "8.5", 0) == NULL) {
return TCL_ERROR;
}
#endif
if (Tcl_PkgProvide(interp, "vtcl", "0.0.1") != TCL_OK)
return TCL_ERROR;
Tcl_CreateNamespace(interp, "vtcl", (ClientData) NULL,
(Tcl_NamespaceDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "::vtcl::recordings", ListRecordingsCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
The management of the Tcl_Obj * reference counts looks absolutely correct, but I do wonder whether you're freeing all the other resources associated with a particular instance in your real code. It might also be something else entirely; your code is not the only thing in Tcl that allocates memory! Furthermore, the default memory allocator in Tcl does not actually return memory to the OS, but instead holds onto it until the process ends. Figuring out what is wrong can be tricky.
You can try doing a build of Tcl with the --enable-symbols=mem passed to configure. That makes Tcl build in an extra command, memory, which allows more extensive checking of memory management behaviour (it also does things like ensure that memory is never written to after it is freed). It's not enabled by default because it has a substantial performance hit, but it could well help you track down what's going on. (The memory info subcommand is where to get started.)
You could also try adding -DPURIFY to the CFLAGS when building; it completely disables the Tcl memory allocator (so memory checking tools like — commercial — Purify and — OSS — Electric Fence can get accurate information, instead of getting very confused by Tcl's high-performance thread-aware allocator) and may allow you to figure out what is going on.
I found where the leak is. In function ListRecordingsCmd, I replaced line
Tcl_AppendObjToObj (cmd, Tcl_NewIntObj (obj_id++));
with
Tcl_Obj *obj = Tcl_NewIntObj (obj_id++);
Tcl_AppendObjToObj (cmd, obj);
Tcl_DecrRefCount(obj);
The memory allocated to store the object id was not released. The memory used by the tclsh process is now stable.

What is the simplest way to retrieve the device serial number of an iOS device using MonoTouch?

Does MonoTouch have a simple mechanism for retrieving the device serial number (not UDID) of an iOS device? Is there a third-party library which I can use to obtain this?
In case it matters, I'm looking to use this functionality in an in-house application and am not concerned with the App Store approval process.
UPDATE: from iOS 8, we cannot retrieve the serial number of our iDevice.
To retrieve iphone serial number from Monotouch, you can use this technic:
Create a static library .a from XCode that have a function to get serial number
In MonoDevelop, create a binding project to bind you .a library into C# classes/functions (http://docs.xamarin.com/guides/ios/advanced_topics/binding_objective-c_libraries)
In your application, you call this binding library (in step 2).
For detail:
STEP 1. In my library.a, I have a class DeviceInfo, here is the implementation to get Serial number
#import "DeviceInfo.h"
#import <dlfcn.h>
#import <mach/port.h>
#import <mach/kern_return.h>
#implementation DeviceInfo
- (NSString *) serialNumber
{
NSString *serialNumber = nil;
void *IOKit = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW);
if (IOKit)
{
mach_port_t *kIOMasterPortDefault = dlsym(IOKit, "kIOMasterPortDefault");
CFMutableDictionaryRef (*IOServiceMatching)(const char *name) = dlsym(IOKit, "IOServiceMatching");
mach_port_t (*IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching) = dlsym(IOKit, "IOServiceGetMatchingService");
CFTypeRef (*IORegistryEntryCreateCFProperty)(mach_port_t entry, CFStringRef key, CFAllocatorRef allocator, uint32_t options) = dlsym(IOKit, "IORegistryEntryCreateCFProperty");
kern_return_t (*IOObjectRelease)(mach_port_t object) = dlsym(IOKit, "IOObjectRelease");
if (kIOMasterPortDefault && IOServiceGetMatchingService && IORegistryEntryCreateCFProperty && IOObjectRelease)
{
mach_port_t platformExpertDevice = IOServiceGetMatchingService(*kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpertDevice)
{
CFTypeRef platformSerialNumber = IORegistryEntryCreateCFProperty(platformExpertDevice, CFSTR("IOPlatformSerialNumber"), kCFAllocatorDefault, 0);
if (CFGetTypeID(platformSerialNumber) == CFStringGetTypeID())
{
serialNumber = [NSString stringWithString:(__bridge NSString*)platformSerialNumber];
CFRelease(platformSerialNumber);
}
IOObjectRelease(platformExpertDevice);
}
}
dlclose(IOKit);
}
return serialNumber;
}
#end
STEP 2. In ApiDefinition.cs of my Binding Library project in Monotouch, I add this binding:
[BaseType (typeof (NSObject))]
public interface DeviceInfo {
[Export ("serialNumber")]
NSString GetSerialNumber ();
}
STEP 3. In my application, I import Reference to Binding library project in step 2, then add
using MyBindingProject;
...
string serialNumber = "";
try {
DeviceInfo nativeDeviceInfo = new DeviceInfo ();
NSString temp = nativeDeviceInfo.GetSerialNumber();
serialNumber = temp.ToString();
} catch (Exception ex) {
Console.WriteLine("Cannot get serial number {0} - {1}",ex.Message, ex.StackTrace);
}
Hope that helps. Don't hesitate if you have any question.

UDP broadcast using CFSocket on IOS

Have been doing some google search and some reading on this subject but cannot seem to get it right no matter how much time i spent searching.
What i want to do is to receive broadcast message of devices that are connected on my network by advertising my interest in their services that they supply. Using wireshark i can see the broadcast/notification messages from the network devices that i want to connect to sent over my network but not my broadcast search for interest of their services.
But with network utility i can see that the socket is created but don't know which state it is in, whether listen or connected.
Yes i know that there are libraries that i can use to do this, but i wanted to build something of my own from the ground up and get a better understand of how it works.
MySocket.h
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if TARGET_OS_IPHONE
#import <CFNetwork/CFNetwork.h>
#endif
#interface MySocket : NSObject
{
NSString* _message;
CFSocketRef cfSocket;
CFRunLoopSourceRef cfSource;
}
- (void)listen;
#end
MySocket.m
#import "MySocket.h"
#define MAX_UDP_DATAGRAM_SIZE 65507
#implementation MySocket
static void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType
type, CFDataRef address, const void *data, void *userInfo)
{
NSLog(#"socketCAllBAck was called");
}
- (void)listen
{
//Enable broadcast to network hosts
int yes = 1;
int setSockResult = 0;
_message = [[NSMutableString alloc ] initWithString: #"M-SEARCH *HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\nMX:1\r\n\r\n"];
CFSocketContext socketContext = {0, self, NULL, NULL, NULL};
/* Create the server socket as a UDP IPv4 socket and set a callback */
/* for calls to the socket's lower-level accept() function */
cfSocket = CFSocketCreate(NULL, PF_INET, SOCK_DGRAM, IPPROTO_UDP,
kCFSocketAcceptCallBack | kCFSocketDataCallBack , (CFSocketCallBack)socketCallback, &socketContext);
if (cfSocket == NULL)
NSLog(#"UDP socket could not be created\n");
/* Re-use local addresses, if they're still in TIME_WAIT */
setSockResult = setsockopt(CFSocketGetNative(cfSocket), SOL_SOCKET, SO_BROADCAST, (void *)&yes, sizeof(yes));
if(setSockResult < 0)
NSLog(#"Could not setsockopt for broabcast");
/* Set the port and address we want to listen on */
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("239.255.255.250");
//inet_pton(AF_INET, "239.255.255.250", &(addr.sin_addr));
addr.sin_port = htons(1900);
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
if (address != nil && CFSocketSetAddress(cfSocket, (CFDataRef) address) != kCFSocketSuccess) {
NSLog(#"CFSocketSetAddress() failed\n");
CFRelease(cfSocket);
}
CFDataRef data = CFDataCreate(NULL, (const UInt8*)[_message UTF8String], [_message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
setSockResult = CFSocketSendData(cfSocket, (CFDataRef)address, data, 0.0);
if(kCFSocketSuccess != setSockResult) NSLog(#"Unable to send data, %i", setSockResult);
else NSLog(#"Sending data");
cfSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, 0);
if(cfSource == NULL)
NSLog(#"CFRunLoopSourceRef is null");
CFRunLoopAddSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
NSLog(#"Socket listening on port 1900");
CFRelease(cfSource);
CFRelease(cfSocket);
[address release];
data = nil;
CFRunLoopRun();
}
- (void)dealloc
{
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
CFRelease(cfSource);
CFRelease(cfSocket);
[_message release];
[super dealloc];
}
#end
Edit: Everything runs well until the call to send data.
Is there something small but critical in order for this to work that i'm missing?
Or i'm missing the big picture?
Any help or guide is appreciated.
Thanks in advance and have a nice weekend
remove SetAdress and all will be work fine. i've tested this now;
How about use this initialization with address
CFDataRef address=CFDataCreate(kCFAllocatorDefault,(UInt8 *)&addr,sizeof(addr));

Resources