UDP broadcast using CFSocket on IOS - 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));

Related

Not getting data callbacks in iOS UDP socket

I'm trying to set up a UDP socket on iOS to listen for datagrams coming over a multicast socket:
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void getSocketDataCallBack (CFSocketRef cfSocketRef, CFSocketCallBackType cbType, CFDataRef address, const void *data, void *info) {
if (cbType == kCFSocketDataCallBack) {
cout << "o";
} else {
cout << "x";
}
}
void main () {
CFSocketError cfErr;
CFSocketContext udpSocketContext = {0, NULL, NULL, NULL, NULL};
udpSocketContext.info = &cbData;
CFSocketRef udpSocketRef = CFSocketCreate(kCFAllocatorDefault,
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
kCFSocketDataCallBack,
&getSocketDataCallBack,
&udpSocketContext);
if ( udpSocketRef == NULL) {
cout << "CFSocketCreate failed\n";
} else {
cout << "UDP socket created\n";
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource( kCFAllocatorDefault, udpSocketRef, 0 );
CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(MC_PORT); //4194
inet_aton(MC_ADDR, &addr.sin_addr); //239.0.123.45
//Tell socket to listen on this address
CFDataRef cfDataRef = CFDataCreate(NULL, (const UInt8 *)&addr, sizeof(addr));
cfErr = CFSocketSetAddress(udpSocketRef, cfDataRef);
}
}
All the socket calls succeed, but I don't get any callbacks (I am sending UDP datagrams to the MC address from a separate macOS application).
What am I doing wrong?
Thanks for any an all assistance!
Cheers.
The problem is that CFSocket does not in itself enable receipt of datagrams sent to an IPv4 (nor IPv6) multicast address. But all is not hopeless!
At https://justanapplication.wordpress.com/category/posix/posix-system-calls/posix-socket/ I found this: "Fortunately the function CFSocketCreateWithNative can turn a theoretical POSIX socket into a theoretical CFSocket." The author of this, Simon Lewis, also says that this actually works, too, "at least on an iPad running iOS 7.0.4," and he is nice enough to provide some code to try it out with. Good Luck!

iOS CFSocket crash on empty UDP packet

I am using a CFSocket configured for UDP to send data through wifi. The problem is that when I receive an empty UDP packet, the wifi suddenly stops working. I cant receive or send wnything anymore after that. Does anyone know what seems to be the problem? Here is my configuration and callback code:
void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType type, CFDataRef address, const void *data, void *userInfo)
{
NSData * inputData = (__bridge NSData*)data;
NSUInteger inputDataCapacity = inputData.length * 2;
if(inputDataCapacity > minimalFrameSize*2){
//DO STH
}
}
-(void) initNetworkCommunication
{
CFSocketContext socketContext = {0, (__bridge void *)(self), NULL, NULL, NULL};
_cfSocket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_DGRAM, IPPROTO_UDP, kCFSocketAcceptCallBack | kCFSocketDataCallBack, (CFSocketCallBack)socketCallback, &socketContext);
if ( _cfSocket == NULL) {
// NSLog(#"CfSocketCreate failed");
}
else {
if( _cfSocket ) {
_addrData = [Utils createIpAddressDataRef:COMM_INT_LISTENER_ADDR];
int flag = 1;
CFSocketNativeHandle socket_handle = (CFSocketNativeHandle)CFSocketGetNative(_cfSocket);
setsockopt(socket_handle, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
//set address of the socket to listen to
CFSocketSetAddress (_cfSocket, _addrData);
_cfSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _cfSocket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), _cfSource, kCFRunLoopDefaultMode);
CFRelease(_cfSource);
CFRelease(_cfSocket);
}
}
}
And sometimes after the wifi "crash" happens, I get an "EXC_BAD_ACCESS" when using CFSocketSendData.

How to Get Host Name connected to a Socket using CFSocketNativeHandle iOS?

I am using Socket programming to connect devices each other in iOS.
I want to get connected device's Host Name which is connected to a socket using CFSocketNativeHandle.
When other device connected to my Socket i get callback in following callback function:
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *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;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
NSData *peer = nil;
if (0 == getpeername(nativeSocketHandle, (struct sockaddr *)name, &namelen)) {
peer = [NSData dataWithBytes:name length:namelen];
NSString *hostName = [[NSString alloc] initWithData:peer encoding:NSUTF8StringEncoding];
NSLog(#"HostName=%#",hostName);
}
}
Here I am getting NSData for "peer" but i am getting NSString *hostName=null.
How can i get exact Host name with this NSData.
getpeername() returns the address of the peer connected to the socket. You can access it using code below:
struct sockaddr_in addr ;
socklen_t len = sizeof(addr) ;
int res = getpeername(nativeSocketHandle, (struct sockaddr *)&addr, &len) ;
if (res == 0) {
char strAddr[20] ;
inet_ntop(AF_INET, &addr.sin_addr, strAddr, sizeof(strAddr)) ;
printf("ip %s\n", strAddr) ;
}
The only thing the socket knows is the IP address of the other end of the connection, so when you use getpeername it returns you the IP address. You will need to look up the IP address using either CFhost or POSIX calls - there are a squillion references to how to accomplish this using the POSIX APIs.
There is a very good chance that the other end will not be the same as the name of the device.

udp server on iOS

I'm trying to send some up sensor data from arduino to my iPhone and i found here (http://www.benripley.com/development/ios/udp-server-on-iphone/) and I run it with some small mods to make it compile on a viewController I display the data and everything works perfectly. But when I leave the view and go back, probably because it tries to start the server again, the app crashes. i tried to move the code to the appDelegate to start the server on launch but the app crashes on launch. even if I try only to start the server on launch and start reading by the time I enter the view and the same thing happens. Any idea?
here is the code:
#include < sys/socket.h >
#include < arpa/inet.h >
#include < errno.h >
#define UDP 17
typedef struct sockaddr_in sockaddr_in;
- (void)startServer {
NSLog(#"UDP Server started...");
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(5009);
// bind the socket to our address
if (-1 == bind(sock,(struct sockaddr *)&sa, sizeof(struct sockaddr)))
{
perror("error bind failed");
close(sock);
exit(EXIT_FAILURE);
}
for (;;)
{
recsize = recvfrom(sock,
(void *)buffer,
1024,
0,
(struct sockaddr *)&sa,
&fromlen);
if (recsize < 0)
fprintf(stderr, "%s\n", strerror(errno));
msg = [NSString stringWithFormat:#"%s", buffer];
NSLog(msg);
}
}
and the actual call is this:
[NSThread detachNewThreadSelector:#selector(startServer)
toTarget:self
withObject:nil];

Is there any way to register a notification when ip address changed on device?

I want to use NSStream to send send and receive information to server.
I thought if I can obtain the ip address the server can send information back in order to replace the silent push notification.
Is is possible to register a notification when ip address changed?
P.S.
I'm currently using reachability class to register a notification when network changed but if there is a better way it'll be great!
I think there is not straight way to achieve this but Use
`[[NSHost currentHost] address];`
save value into userDefault and every time when you connect check with stored value and get notify
i found this on SO hope this work for you
#include <ifaddrs.h>
#include <arpa/inet.h>
- (NSString *)getIPAddress {
NSString *address = #"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 0;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0) {
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL) {
if(temp_addr->ifa_addr->sa_family == AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:#"en0"]) {
// Get NSString from C String
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return address;
}

Resources