Sending udp packets in iOS 6 - iphone

I am struggling to get anything to send from the iphone, I followed This Guide At first I started off with cfSocketRef as I thought you used them for UDP and and TCP by changing protocol but I had no luck.
So here is the code for the BSD sockets. Nothing seems to be sending. I have a java socket server waiting on localhost:port.
Any ideas? or maybe a guide/sample xcode project that works.
#import "ViewController.h"
#include <CFNetwork/CFNetwork.h> //temp //dont leave here put it in a header
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#interface ViewController ()
#end
#implementation ViewController
static void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType
type, CFDataRef address, const void *data, void *userInfo)
{
NSLog(#"socketCallback called");
}
//
- (void)viewDidLoad
{
[super viewDidLoad];
int sock = 0; /// ?
unsigned int echolen;
NSLog(#"starting udp testing");
cfSocketRef = CFSocketCreate(/*CFAllocatorRef allocator*/ NULL,
/*SInt32 protocolFamily*/ PF_INET,
/*SInt32 socketType*/ SOCK_DGRAM,
/*SInt32 protocol*/ IPPROTO_UDP,
/*CFOptionFlags callBackTypes*/ kCFSocketAcceptCallBack | kCFSocketDataCallBack,
/*CFSocketCallBack callout*/ (CFSocketCallBack)socketCallback,
/*const CFSocketContext *context*/ NULL);
struct sockaddr_in destination;
memset(&destination, 0, sizeof(struct sockaddr_in));
destination.sin_len = sizeof(struct sockaddr_in);
destination.sin_family = AF_INET;
NSString *ip = #"localhost";
destination.sin_addr.s_addr = inet_addr([ip UTF8String]);
destination.sin_port = htons(33033); //port
NSString *msg = #"message sent from iPhone";
/* server port */
setsockopt(sock,
IPPROTO_IP,
IP_MULTICAST_IF,
&destination,
sizeof(destination));
const char *cmsg = [msg UTF8String];
echolen = strlen(cmsg);
if (sendto(sock,
cmsg,
echolen,
0,
(struct sockaddr *) &destination,
sizeof(destination)) != echolen)
{
NSLog(#"did send");
}
else
{
NSLog(#"did not send");
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

First problem: You forgot to create the socket:
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
NSLog(#"Failed to create socket, error=%s", strerror(errno));
}
This part does not work:
NSString *ip = #"localhost";
destination.sin_addr.s_addr = inet_addr([ip UTF8String]);
inet_addr converts strings representing IPv4 addresses in the dot notation, such as "127.0.0.1".
To convert a host name such as "localhost" to an IP address, you have to use gethostbyname, or getaddrinfo (the latter works with IPv4 and IPv6).
There is another error when you check the return value of sendto: sendto returns the number of bytes sent in the success case, and (-1) in error case. So it should look like:
if (sendto(sock, ...) == -1) {
NSLog(#"did not send, error=%s",strerror(errno));
} else {
NSLog(#"did send");
}
Checking the value of errno would have revealed your problem quickly. If you forget to create the socket, the error message is
did not send, error=Socket operation on non-socket
Remarks:
cfSocketRef is completely unused in your function.
Why do you set the IP_MULTICAST_IF socket option? As far as I know, that is not needed for unicast messages, only for multicast messages.

Related

How to get ip address from NetService

When I get a NetService object,I try to do:
NSNetService *ss=[netArray objectAtIndex:indexPath.row];
ss.delegate=self;
[ss resolveWithTimeout:3.0];
On the delegate method:
-(void)netServiceDidResolveAddress:(NSNetService *)sender
{
NSArray *address=sender.addresses;
NSData *addressData=[NSData dataWithBytes:address length:sizeof(address)];
/*
How?
*/
}
Thanks.
// Sent when addresses are resolved
- (void)netServiceDidResolveAddress:(NSNetService *)netService
{
// Make sure [netService addresses] contains the
// necessary connection information
if ([self addressesComplete:[netService addresses]
forServiceType:[netService type]]) {
[services addObject:netService];
}
}
// Verifies [netService addresses]
- (BOOL)addressesComplete:(NSArray *)addresses
forServiceType:(NSString *)serviceType
{
// Perform appropriate logic to ensure that [netService addresses]
// contains the appropriate information to connect to the service
NSData *myData = nil;
myData = [addresses objectAtIndex:0];
NSString *addressString;
int port=0;
struct sockaddr *addressGeneric;
struct sockaddr_in addressClient;
addressGeneric = (struct sockaddr *) [myData bytes];
switch( addressGeneric->sa_family ) {
case AF_INET: {
struct sockaddr_in *ip4;
char dest[INET_ADDRSTRLEN];
ip4 = (struct sockaddr_in *) [myData bytes];
port = ntohs(ip4->sin_port);
addressString = [NSString stringWithFormat: #"IP4: %s Port: %d", inet_ntop(AF_INET, &ip4->sin_addr, dest, sizeof dest),port];
}
break;
case AF_INET6: {
struct sockaddr_in6 *ip6;
char dest[INET6_ADDRSTRLEN];
ip6 = (struct sockaddr_in6 *) [myData bytes];
port = ntohs(ip6->sin6_port);
addressString = [NSString stringWithFormat: #"IP6: %s Port: %d", inet_ntop(AF_INET6, &ip6->sin6_addr, dest, sizeof dest),port];
}
break;
default:
addressString=#"Unknown family";
break;
}
NSLog(#"Client Address: %#",addressString);
return YES;
}
This is the output
Client Address: IP4: 192.168.69.38 Port: 58612
I found a post which suggests the following solution.
NSString* addressString = [[NSString alloc] initWithData:addressData encoding:NSASCIIStringEncoding];
Though it does not output a human readable string for me... It might work for you.
This is what is written to the console when I print the NSData object.
<10026a5e 0a0a7893 00000000 00000000>
Meanwhile, I found out that the second segment is the hexadecimal form of the ip-address. In this example it is ...
10.10.120.147 // 0a0a7893
I have written a Host class that does the conversion. The NSString extension can be found here. I only use the first 16-bytes address and ignore all others. Feel free to extend the class.

iphone socket disconnecting after 1 min

We have following code for connecting to our server. This is part of iPhone App.
Problem is that recv(CFSocketGetNative(inSocketRef), &length, sizeof(length), 0);
call returns 0 after exactly 60 sec. We are not sending anything from server. I want it to wait for data or disconnection (either server or client initiated). However, it always returns after 60 sec.
What am I doing wrong here?
void CallbackHandlerConnectionHandler(CFSocketRef inSocketRef, CFSocketCallBackType inType,
CFDataRef inAddress, const void *inData, void *inInfo)
{
ChatServerConnectionHandler *conHandler = (ChatServerConnectionHandler *)inInfo;
if([conHandler respondsToSelector:#selector(dataRecievedFromServer:)])
{
int res = 0;
SInt32 length;
res = recv(CFSocketGetNative(inSocketRef), &length, sizeof(length), MSG_PEEK);
if(0 >= res || (length < 0))
{
//Disconnect the connection, as some has occcured in the sockets..
[conHandler performSelector:#selector(errorEncountered)];
NSLog(#"Error occured in server!!");
return;
}
printf ("good data")
}
}
-(BOOL) connectToServer:(NSString *)ipAddress Port:(int)portNumber
{
connectionState = eConnectionEstablishInProgress;
self.threadStopped = NO;
CFRunLoopSourceRef source;
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *host;
host = gethostbyname([ipAddress cStringUsingEncoding:NSUTF8StringEncoding]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct timeval timeout;
if (sockfd < 0)
{
error("ERROR opening socket");
connectionState = eNoConnection;
return NO;
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portNumber);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
/* Creates a CFSocket object for a pre-existing native socket */
CFSocketContext socketContext={0,self,NULL,NULL,NULL};
socketRef = CFSocketCreateWithNative(kCFAllocatorDefault,
sockfd,
kCFSocketReadCallBack,
CallbackHandlerConnectionHandler,
&socketContext);
source = CFSocketCreateRunLoopSource(NULL, socketRef, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
source = nil;
InstallSignalHandlers();
CFDataRef socketAddress;
socketAddress = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (UInt8 *)&serv_addr, sizeof(serv_addr), kCFAllocatorNull);
CFSocketError result=CFSocketConnectToAddress(socketRef, socketAddress, 0);
CFRelease(socketAddress);
if(kCFSocketSuccess == result)
{
//printf("Socket connection established");
self.serverRunning = YES;
connectionState = eConnectionEstablised;
NSLog(#"\nCall Connection accepted callback.");
if([delegate respondsToSelector:#selector(conectionEstablishedWithTheServer)])
{
[delegate performSelector:#selector(conectionEstablishedWithTheServer)];
}
}
else
{
//printf("Unable to create socket why???***");
connectionState = eNoConnection;
if(socketRef)
CFRelease(socketRef);
socketRef = nil;
return NO;
}
while(FALSE == self.threadStopped)
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, FALSE);
}
NSLog(#"\nOut of Server socket connection thread.");
self.serverRunning = NO;
if([delegate respondsToSelector:#selector(conectionFinishedWithServer)])
{
[delegate performSelector:#selector(conectionFinishedWithServer)];
}
if(socketRef)
{
if(CFSocketIsValid(socketRef))
CFSocketInvalidate(socketRef);
}
return YES;
}
This looks like a server problem - or rather functionality: closing inactive connections to keep enough free resources.
If you didn't write the server code try to contact the author.
The packet you mention [FIN, ACK] is a packet that closes your socket: you should check the IP adresses but the server is most probably the initiator of this packet.

Client not discovering bonjour service

I have just started learning to design iphone apps and I am trying to set up a client server environment. As a starter I would like to first make sure that the service I publish is visible to the client. I have written server and client codes wit help from Apple's Docs. However I find that though my service is getting published (netServiceWillPublish(NSNetService *)sender is called), my client is not identifying this service. At the client's side the delegate function netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser is called making me believe that the client is searching for the service. However the didFindService delegate function is never called. i am stumped as to why this is not working. I have followed the sample codes for the client side from various places and my code matches theirs. Some help on this would be greatly appreciated
Also I have the following questions
Can the service name (that comes along with the transport level protocol) be any string? Or does it have to be specified in http://www.dns-sd.org/ServiceTypes.html. Also what purpose does this solve apart from service discovery.
When I print out the details of the NSNetService object at the server's side (After setting up the socket and starting the service) the hostname is printed as (null) and there are no addresses being displayed. technically when a service is opened shouldn't these be set to values? If yes could someone please tell me where I am going wrong in my code?
I have attached the client and server code blocks of my project where the publishing and browsing of servicing is done. Help is extremely appreciated as I am stuck in my work with this issue.
This is the code snippet at the client side that browses for the services
NSNetServiceBrowser *serviceBrowser;
serviceBrowser = [[NSNetServiceBrowser alloc] init];
[serviceBrowser setDelegate:self];
[serviceBrowser searchForServicesOfType:#"_http._tcp." inDomain:#"local."];
This is the code snippet at the server side that publishes the service
BOOL success;
int fd;
int err;
int junk;
struct sockaddr_in addr;
NSInteger port;
port = 0;
fd = socket(AF_INET, SOCK_STREAM, 0);
success = (fd != -1);
if(success) {
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = INADDR_ANY;
err = bind(fd, (const struct sockaddr*) &addr, sizeof(addr));
success = (err == 0);
}
if (success) {
err = listen(fd, 5);
success = (err == 0);
}
if (success) {
socklen_t addrLen;
addrLen = sizeof(addr);
err = getsockname(fd, (struct sockaddr *) &addr, &addrLen);
success = (err == 0);
if (success) {
assert( addrLen = sizeof(addr));
port = ntohs(addr.sin_port);
}
}
if (success) {
CFSocketContext context = { 0, (__bridge void *) self, NULL, NULL, NULL };
assert(self->_listeningSocket == NULL);
self->_listeningSocket = CFSocketCreateWithNative(NULL, fd, kCFSocketAcceptCallBack, AcceptCallBack, &context);
success = (self->_listeningSocket != NULL);
if (success) {
CFRunLoopSourceRef rls;
fd = -1; //listeningSocket is now responsible for closing the socket
rls = CFSocketCreateRunLoopSource(NULL, self.listeningSocket, 0);
assert(rls != NULL);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
}
}
if (success) {
self.netService = [[NSNetService alloc] initWithDomain:#"local." type:#"_http._tcp." name:#"test" port:port];
success = (self.netService != nil);
}
if (success) {
self.netService.delegate = self;
[self.netService publishWithOptions:NSNetServiceNoAutoRename];
}
if (success) {
assert(port != 0);
[self serverDidStartOnPort:port];
}
else {
[self stopServer:#"Start Failed"];
if (fd != -1) {
junk = close(fd);
assert(junk == 0);
}
}
This is the code snippet that tells that the service is published and prints out the details of my NSSocket object
- (void)netServiceWillPublish:(NSNetService *)sender {
NSLog(#"This function is getting called");
NSLog(#"sender.name %#",sender.name);
NSLog(#"sender.addresses %#",sender.addresses);
NSLog(#"sender.domain %#",sender.domain);
NSLog(#"sender.hostname %#",sender.hostName);
NSLog(#"sender.type %#",sender.type);
NSLog(#"sender.port %d",sender.port);
}
Thanks
Vivek

Get IP address of the current Wi-Fi access point on a iPhone?

I am trying to connect from my iPhone to a socket server running on the same network on my computer. How can I get the local IP address of the computer that is running the socket server?
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.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;
}
Your server needs to broadcast itself using the mDNS protocol.
Then on the iPhone, you can simply use gethostbyname to get the IP address. You also have a specific API for that: DNS Service Discovery C Reference.

iPhone: Bonjour NSNetService IP address and port

Excuse my iPhone/Objective-C newbie status please!
I've found my HTTP server using NSNetServiceBrowser, but now I just want the IP address and port of the service found.
I've got something like the following in my delegate method:
NSNetService* server = [serverBrowser.servers objectAtIndex:0];
NSString *name = nil;
NSData *address = nil;
struct sockaddr_in *socketAddress = nil;
NSString *ipString = nil;
int port;
uint i;
for (i = 0; i < [[server addresses] count]; i++)
{
name = [server name];
address = [[server addresses] objectAtIndex:i];
socketAddress = (struct sockaddr_in *)
[address bytes];
ipString = [NSString stringWithFormat: #"%s",
inet_ntoa (socketAddress->sin_addr)];
port = socketAddress->sin_port;
NSLog(#"Server found is %s %d",ipString,port);
}
but the for loop is never entered, even though the delegate is called. Any ideas? Thanks!
I realize this is an old thread, but I've just run across this as well. There are a few problems with the code above:
It's not IPv6 savvy. At a
minimum, it should detect and
discard IPv6 addresses if the rest
of your app can only handle v4
addresses, but ideally you should be
prepared to pass both address
families upstream.
The port assignment will
generate incorrect values for Intel
processors. You need to use htons
to fix that.
As Andrew noted above, the
iteration should use the enhanced
for loop.
(EDIT: Added this) As noted on another related thread, the use of inet_ntoa is discouraged in favor of inet_ntop.
Putting all of this together, you get:
char addressBuffer[INET6_ADDRSTRLEN];
for (NSData *data in self.addresses)
{
memset(addressBuffer, 0, INET6_ADDRSTRLEN);
typedef union {
struct sockaddr sa;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
} ip_socket_address;
ip_socket_address *socketAddress = (ip_socket_address *)[data bytes];
if (socketAddress && (socketAddress->sa.sa_family == AF_INET || socketAddress->sa.sa_family == AF_INET6))
{
const char *addressStr = inet_ntop(
socketAddress->sa.sa_family,
(socketAddress->sa.sa_family == AF_INET ? (void *)&(socketAddress->ipv4.sin_addr) : (void *)&(socketAddress->ipv6.sin6_addr)),
addressBuffer,
sizeof(addressBuffer));
int port = ntohs(socketAddress->sa.sa_family == AF_INET ? socketAddress->ipv4.sin_port : socketAddress->ipv6.sin6_port);
if (addressStr && port)
{
NSLog(#"Found service at %s:%d", addressStr, port);
}
}
}
The NSNetService you get back in the callback isn't ready to be used. You have to call the following method to get addresses for it:
- (void)resolveWithTimeout:(NSTimeInterval)timeout;
Implement the NSNetService delegate method to find out when it resolves:
- (void)netServiceDidResolveAddress:(NSNetService *)sender;
At that point, there should be at least one address in the service.
Also, take care to read the documentation and the header file carefully! There is some complexity to the issue here that I've glossed over.
Remix of the accepted answer in a category:
NSNetService+Util.h
#import <Foundation/Foundation.h>
#interface NSNetService (Util)
- (NSArray*) addressesAndPorts;
#end
#interface AddressAndPort : NSObject
#property (nonatomic, assign) int port;
#property (nonatomic, strong) NSString *address;
#end
NSNetService+Util.m
#import "NSNetService+Util.h"
#include <arpa/inet.h>
#implementation NSNetService (Util)
- (NSArray*) addressesAndPorts {
// this came from http://stackoverflow.com/a/4976808/8047
NSMutableArray *retVal = [NSMutableArray array];
char addressBuffer[INET6_ADDRSTRLEN];
for (NSData *data in self.addresses)
{
memset(addressBuffer, 0, INET6_ADDRSTRLEN);
typedef union {
struct sockaddr sa;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
} ip_socket_address;
ip_socket_address *socketAddress = (ip_socket_address *)[data bytes];
if (socketAddress && (socketAddress->sa.sa_family == AF_INET || socketAddress->sa.sa_family == AF_INET6))
{
const char *addressStr = inet_ntop(
socketAddress->sa.sa_family,
(socketAddress->sa.sa_family == AF_INET ? (void *)&(socketAddress->ipv4.sin_addr) : (void *)&(socketAddress->ipv6.sin6_addr)),
addressBuffer,
sizeof(addressBuffer));
int port = ntohs(socketAddress->sa.sa_family == AF_INET ? socketAddress->ipv4.sin_port : socketAddress->ipv6.sin6_port);
if (addressStr && port)
{
AddressAndPort *aAndP = [[AddressAndPort alloc] init];
aAndP.address = [NSString stringWithCString:addressStr encoding:kCFStringEncodingUTF8];
aAndP.port = port;
[retVal addObject:aAndP];
}
}
}
return retVal;
}
#end
#implementation AddressAndPort
#end
[Yes, I have no fear of creating lots of NSObject instances...]