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...]
Related
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.
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.
I just like to ask if it is possible to identify the IP Address of a device (e.g. a printer) using its Hostname even if its Bonjour setting is turned off? Also can you give me an example on how to do it? I am developing an app in iOS that should handle this scenario.
I have looked at the following:
getaddrinfo
CFHostStartInfoResolution
but they work only if the device's bonjour is turned ON.
Assuming the hostname (let's say nameOfTheDevice) is registered with the zone's authoritative DNS server, you can use CFHost to look up an address or hostname. For example:
NSString* hostname = #"nameOfTheDevice";
CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)hostname);
Boolean lookup = CFHostStartInfoResolution(hostRef, kCFHostAddresses, NULL);
NSArray* addresses = (NSArray*)CFHostGetAddressing(hostRef, &lookup);
[addresses enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *strDNS = [NSString stringWithUTF8String:inet_ntoa(*((struct in_addr *)obj))];
NSLog(#"Resolved %d->%#", idx, strDNS);
}];
(Remember to put error checks in your production code). Bear in mind that if the DNS server isn't aware of that hostname, there's nothing you can do. It's not safe to assume that you'll be able to perform a successful lookup, especially on a home network where built-in DHCP/DNS servers have widely varying capabilities.
From previous answer get callback func and pass obj as a parameter into this function
void printAddr(CFDataRef address)
{
NSString *addressString;
struct sockaddr *addressGeneric;
NSData *myData = (__bridge NSData*)address;
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];
addressString = [NSString stringWithFormat: #"IP4: %s", inet_ntop(AF_INET, &ip4->sin_addr, dest, sizeof dest)];
}
break;
case AF_INET6: {
struct sockaddr_in6 *ip6;
char dest[INET6_ADDRSTRLEN];
ip6 = (struct sockaddr_in6 *) [myData bytes];
addressString = [NSString stringWithFormat: #"IP6: %s", inet_ntop(AF_INET6, &ip6->sin6_addr, dest, sizeof dest)];
}
break;
}
NSLog(#"%#", addressString);
}
I am working in a little app for iphone base on ideas used to make an Android app.
To test, obviously i use the simulator, but the simulator don't have support for built-in camera. The Android idea to test this consist in use a WebCamBroadcaster Java app in the desktop to capture frames from built-in webcam and pass it through socket. Then in the app you just read the bytes and convert to image.
Well i was trying to do the same thing with iPhone Simulator. Searching in the web a found a class to work with asynchronous sockets (cocoaasyncsocket). But i can't make it work.
The Java App send the frames like this:
socket = ss.accept();
BufferedImage image = videoCapture.getNextImage();
if (image != null) {
OutputStream out = socket.getOutputStream();
if (RAW) {
image.getWritableTile(0, 0).getDataElements(0, 0, w$
image.releaseWritableTile(0, 0);
DataOutputStream dout = new DataOutputStream(new Bu$
out));
for (int i = 0; i < data.length; i++) {
dout.writeInt(data[i]);
}
dout.close();
} else {
ImageIO.write(image, "JPEG", out);
}
}
The Android version of this use C code to implement de socket reading proccess like this:
long read_count, total_read = 0;
while (total_read < readBufSize)
{
read_count = read(sockd, &readBuf[total_read], readBufSize);
if (read_count <= 0 || errno != 0)
{
char buffer[100];
sprintf(buffer, "socket read errorno = %d", errno);
LOGV(buffer);
break;
}
total_read += read_count;
}
// If we read all of the data we expected, we will load the frame from the p$
if (total_read == readBufSize){
frame = loadPixels(readBuf, width, height);}
Where readBufsize = width*height*sizeof(int);
readBuf = (char*)malloc(readBufSize);
So i try to implement the same for iPhone but i have an error in the connection (errno = 2).. Then i find cocoaasyncsocket and i try to use but i have an unknown error and nothing is read:
#import <Foundation/Foundation.h>
#import "AsyncSocket.h"
#interface Captura : NSObject {
NSString *ipserver;
UInt16 port;
NSError *errPtr;
AsyncSocket *socket;
NSMutableData *socketData;
}
#property (nonatomic,retain) NSString *ipserver;
#property (retain) AsyncSocket *socket;
#property (retain) NSError *errPtr;
//will contain de data read from socket
#property (retain) NSMutableData *socketData;
-(id)initWithIp:(NSString*)ip puerto:(UInt16)p;
-(BOOL)open;
-(void)close;
-(void)beginRead;
- (UIImage*)getImage;
#end
and the implementation
#import "Captura.h"
#implementation Captura
#synthesize ipserver;
#synthesize socket;
#synthesize errPtr;
#synthesize socketData;
-(id)initWithIp:(NSString*)ip puerto:(UInt16)p{
if (self = [super init]) {
ipserver = ip;
port = p;
socket = [[AsyncSocket alloc] initWithDelegate:self];
socketData = [[NSMutableData alloc] init];
}
return self;
}
//Connect
-(BOOL)open{
return [socket connectToHost:ipserver onPort:port error:&errPtr];
}
-(void)beginRead{
NSLog(#"Begin Read");
NSUInteger offset = [socketData length];
[socket readDataWithTimeout:1
tag:0];
}
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{
NSLog(#"Conectado al servidor");
}
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSLog(#"Data leida %u",[data length]);
[socketData appendData:data];
[self beginRead];
}
- (void)onSocketDidDisconnect:(AsyncSocket *)sock{
[socketData release];
[ipserver release];
[socket release];
NSLog(#"MutableData length %u", [socketData length]);
NSLog(#"Socket Desconectado");
}
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err{
NSLog(#"Ocurrió un error desconectando.... %#",err);
}
- (UIImage*)getImage{
NSData *data;
[socketData getBytes:data length:320*480*sizeof(int)];
NSLog(#"Data obtenida %#",[data length]);
if ([socketData length]>320*480*sizeof(int)) {
[socketData replaceBytesInRange:NSMakeRange(0,320*480*sizeof(int)) withBytes:NULL length:0];
}
if (data!=nil && [data length]) {
UIImage *img = [[UIImage alloc] initWithData:data];
[data release];
return img;
}
[data release];
return nil;
}
#end
Well this code connect to the server and initialize the reading process and then close up.. socket is disconnect and the app is close.
i can't test de getImage method yet...
Some idea?
Thanks in advance...
I think you need a call to -beginRead in -onSocket:didConnectToHost:port:
I want to find IP address in an application. I am able to find it. But, problem is, it works fins in iphone os 2.0 or so. But, in iphone os 3.0 it is giving me a warning:
warning: no '+currentHost' method found
warning: (Messages without a matching method signature)
I am using this code, and it works fine with os version 2.0.
-(NSString*)getAddress {
char iphone_ip[255];
strcpy(iphone_ip,"127.0.0.1"); // if everything fails
NSHost* myhost = [NSHost currentHost];
if (myhost)
{
NSString *ad = [myhost address];
if (ad)
strcpy(iphone_ip,[ad cStringUsingEncoding: NSISOLatin1StringEncoding]);
}
return [NSString stringWithFormat:#"%s",iphone_ip];
}
How to find IP address in iphone os 3.0 or greater os version?
Thanks in advance.
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <ifaddrs.h>
// retun the host name
+ (NSString *)hostname
{
char baseHostName[256];
int success = gethostname(baseHostName, 255);
if (success != 0) return nil;
baseHostName[255] = '\0';
#if !TARGET_IPHONE_SIMULATOR
return [NSString stringWithFormat:#"%s.local", baseHostName];
#else
return [NSString stringWithFormat:#"%s", baseHostName];
#endif
}
// return IP Address
+ (NSString *)localIPAddress
{
struct hostent *host = gethostbyname([[self hostname] UTF8String]);
if (!host) {herror("resolv"); return nil;}
struct in_addr **list = (struct in_addr **)host->h_addr_list;
return [NSString stringWithCString:inet_ntoa(*list[0]) encoding:NSUTF8StringEncoding];
}
As far as I know there is only one hacky way to do that. You basically open a socket and get its address using POSIX functions. Here is the code I used for this:
http://iphonesdksnippets.com/post/2009/09/07/Get-IP-address-of-iPhone.aspx
[NSHost currentHost] will also work, but it is deprecated and considered a "Private API" by Apple, so you won't be able to submit your application to App Store.
Put this script on a web server running PHP:
<?php
$ip = getenv("REMOTE_ADDR");
echo $ip;
?>
Call this on the device:
NSURL *scriptURL = [[NSURL alloc] initWithString:#"http://yourserver.com/script.php"];
NSString *ip = [NSString stringWithContentsOfURL: scriptURL encoding:NSASCIIStringEncoding error:nil];
- (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
// it may also be en1 on your ipad3.
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;
}
Use this to get your IP
If any errors
Please use
#include <ifaddrs.h>
#include <arpa/inet.h>
Getting the IP address is a bit hacky. Are you sure you couldn't live with the device ID (UDID) that is unique to each iPhone and can be retrieved easily via the public API ?
[UIDevice currentDevice].uniqueIdentifier
There is one more way to get the IP address and that too Global IP
NSString* ip=#"http://www.whatismyip.org/";
NSURL *url = [[NSURL alloc] initWithString:ip];
NSString *ans = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&error];
NSLog(#"%#",ans);
The above site will give you the Global IP.
Just put this in your code and use the IP address where ever you want and also get the location of the user using your app as this gives global IP.
You should have a look to this good project:
uidevice-extension
Specialy this class
Import UIDevice-Reachability.h in your project then try with one of those commands:
NSString *myIp = [UIDevice localIPAddress];
NSString *myIp = [UIDevice localWiFiIPAddress];
NSString *myIp = [UIDevice whatismyipdotcom]; // is using #aamritrao solution
bool success;
struct ifaddrs *addrs;
const struct ifaddrs *cursor;
const struct sockaddr_dl *dlAddr;
const uint8_t *base;
int i;
success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != NULL) {
if ( (cursor->ifa_addr->sa_family == AF_LINK)
&& (((const struct sockaddr_dl *) cursor->ifa_addr)->sdl_type ==IFT_ETHER)
) {
dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
// fprintf(stderr, " sdl_nlen = %d\n", dlAddr->sdl_nlen);
// fprintf(stderr, " sdl_alen = %d\n", dlAddr->sdl_alen);
base = (const uint8_t *) &dlAddr->sdl_data[dlAddr->sdl_nlen];
printf(" MAC address ");
for (i = 0; i < dlAddr->sdl_alen; i++) {
if (i != 0) {
printf(":");
}
printf("%02x", base[i]);
}
printf("\n");
}
cursor = cursor->ifa_next;
}
}