No Writing posible: SSL / TLS with AsyncSocket for iOS - iphone

I am working with the AsyncSocket for iOS. (https://github.com/robbiehanson/CocoaAsyncSocket)
I wrote my own Class to handle the connection with AsyncSocket. I wrote the method writeDate: (NSString *) data and add the AsyncSocket Callbackfunctions in my Class.
-(void)writeData: (NSString*) data
{
NSString *test = [NSString stringWithFormat:#"test"];
NSData *raw = [test dataUsingEncoding:NSUTF8StringEncoding];
[socket writeData:raw withTimeout:-1 tag:1];
}
The connection works great and if I'm trying to send a message in "didConnectToHost" after [sock startTLS:setting] it works.
But if I try to send a message with my own method (writeDate: (NSString *) data) and the call:
MyClass *test = [MyClass sharedSSLConnection];
[test writeData:#"test"];
It failed. I think the problem is, that the Handshake isn't ready when the method want's to sent a message on the socket. But how can I solve this? I did not find any solution.
Thanks for your help!

Might recommend that your MyClass store the data and wait until the state is ready onSocketDidSecure and send it then?

Related

AsyncSockets - ReadToData - Doesn't work like expected

It's my first specific question here on stackoverflow, cause I couldn't found any helpful solutions for my problem yet.
I need a low level socket connection between my iPhone and OSX Workstation (as TCP Server), to interchange some media data like pictures or audio files. So I think AsyncSockets is a good choise to get this to work. I've often used it for some tiny byte communication.
My Problem is, that I want to use a kind of a header/protocol to tell the server how much data bytes are still in pipe.
A simple communication like "hello world" is working fine, so there are no connection problems.
The mobile device (that wants to send a picture) does the following.
[self setHost:#"172.22.42.207"];
self.socket = [[[AsyncSocket alloc] initWithDelegate:self] autorelease];
NSError *err = nil;
[[self socket] connectToHost:self.host onPort:5009 error:&err];
...
NSData *t = UIImagePNGRepresentation(test);
NSString *header = [NSString stringWithFormat:#"%i", t.length];
NSMutableData *headerData = [[header dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
[headerData appendBytes:[AsyncSocket CRLFData] length:[[AsyncSocket CRLFData] length]];
[[self socket] writeData:headerData withTimeout:-1 tag:0];
The server is listening that way:
AsyncSocket *s = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
[s acceptOnPort:5009 error:&err];
if(err)
NSLog(#"EPIC FAIL...\n%#", err);
....
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[newSocket readDataToData:[AsyncSocket CRLFData] withTimeout:-1 tag:0];
}
Now: If I use readData:withTimeout:tag it all works like a charm. But once I change the code to readDataToData:withTimeout:tag, to split the header from the other content, the onSocket:didConnectToHost:port: method is never called. Here are some pretty function logs (I placed them in every delegate method)
Client side:
2012-01-31 13:40:32.962 AVMobile[20643:10703] -[SLViewController onSocket:didConnectToHost:port:]
2012-01-31 13:40:32.964 AVMobile[20643:10703] -[SLViewController onSocket:didWriteDataWithTag:]
Server side:
2012-01-31 13:40:32.961 AVServer[20618:707] -[SLAppDelegate onSocket:didAcceptNewSocket:]
So, next idea... just compare the sending and receiving bytes, so:
Sending: <33333736 35365cba>
Receiving: <33333736 35365cba>
Yeah... now my final question: What am I doing wrong!?
Why isn't it working out for me :)?
Greetings & thanks!
sniperosx
Found a solution:
Just don't use -1 as timeout.
With -1 timeout the AsyncSocket is reading data until the other side is closing the connection, so in this range no delegate method is called.
Cheerz
sniperosx
[Closed]

How to return data gotten from a web service in objective- c (iPhone)?

This might be a dumb question. Sorry if it is.
But Im working on a project that consumes web services. I can connect to the web service and get the data I need fine.
I would like to have a method that returns this data obtained from the web service to the caller. The only problem is that the data is only obtained inside the ConnectionDidFinishLoading method, and I can't access this data from my method.
here is my code, that works fine:
- (NSData *) dataForMethod:(NSString *)webMethod withPostString:(NSString *)postString
{
NSURL *url = [NSURL URLWithString:[SigameWebServiceAddress stringByAppendingFormat:#"%#%#", #"/", webMethod]];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [postString length]];
[req addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [postString dataUsingEncoding:NSUTF8StringEncoding]];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [NSMutableData data];
}
// I WOULD LIKE TO RETURN WEBDATA TO THE CALLER HERE, BUT WEBDATA IS EMPTY NOW, THE
//connectionDidFinishLoading ONLY GETS CALLED WITH THE DATA I WANT AFTER THE COMPILER
//IS DONE EXECUTING MY METHOD.
}
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response
{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
NSLog(#"FATAL ERROR");
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
//---shows the XML---
NSLog(#"%#", theXML); //NOW, THIS IS THE DATA I WANT. BUT HOW CAN I RETURN THIS TO
//THE CALLER. I MEAN, THE CALLER THAT CALLED MY METHOD
//+ (NSData *) dataForMethod: withPostString:
}
Any help here is appreciated!
Thanks
There are really two ways to go about this.
Create a delegate interface
Use Blocks
I would strongly advise against using the synchronous methods - unless you are/have created your own asynchronous framework around them (i.e. you are manually starting another thread and executing your synchronous request on that thread). In the long run you will realize you need the requests to be async, and you'll have to re-work everything such that they are.
To give a quick overview of the two options I gave:
1. Create a delegate interface
The idea here is to create a class which performs the request, and create a protocol the caller must implement. When the request is complete, you will invoke a specified method on the delegate with the data:
The protocol might look something like this:
#protocol RequestClassDelegate <NSObject>
- (void)requestCompleted:(ResponseClass *)data;
- (void)requestError:(NSError *)error;
#end
The class which makes the request might look something like this:
#interface RequestClass : NSObject
- (void)makeRequest:(id<RequestClassDelegate>)delegate;
#end
And the request class implementation might contain some of the following, in addition to your connection logic:
#implementation RequestClass
{
__weak id<RequestClassDelegate> _delegate;
}
// Connection Logic, etc.
- (void)makeRequest:(id<RequestClassDelegate>)delegate
{
_delegate = delegate;
// Initiate the request...
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
// Processing, etc.
// Here we'll call the delegate with the result:
[_delegate requestCompleted:theResult];
}
#end
2. Use Blocks
This solution is much the same as the first solution - but, a bit more elegant in my opinion. Here, we'll change the RequestClass to use blocks instead of a delegate:
typedef void (^requestCompletedBlock)(id);
typedef void (^requestErrorBlock)(NSError *);
#interface RequestClass : NSObject
#property (nonatomic, copy) requestCompletedBlock completed;
#property (nonatomic, copy) requestErrorBlock errored;
- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error;
#end
And the implementation of that might look something like this:
#implementation RequestClass
#synthesize completed = _completed;
#synthesize errored = _errored;
// Connection Logic, etc.
- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error
{
self.completed = completed;
self.errored = error;
// Initiate the request...
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
// Processing, etc.
// Here we'll call the delegate with the result:
self.completed(theResult);
}
#end
It sounds like you are trying to use return the data synchronously from your method, but you are using an asynchronous method (using an NSURLConnection and presumably calling its start method) to begin retrieving data. If you really want your method to return its result synchronously, read on. As #Steve says in another answer, however, you may also reconsider your interface design and instead implement it using an asynchronous approach and use his recommendations for either a delegate or block-based interface.
If you want to return the data synchronously from your method, use a synchronous request. So change this part of your code:
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
[conn start]; // I presume you have this somewhere
if (conn)
{
webData = [NSMutableData data];
}
with something more like this:
NSURLResponse *response = nil;
NSError *error = nil;
webdata = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error];
if (webdata) {
return webdata;
}
else {
// Handle error by looking at response and/or error values
return nil;
}
You will no longer need any of your delegate code if you use this approach. You will be limited in some ways though. For example, if your web service requires authentication via something other than URL parameters you can't use this approach.
Steve's answer is great and I can only suggest the way using blocks. Actually, as I am new into Objective-C I implemented the approach steve outlined. It works perfectly.
The Post for more details and my own point of view you can find here:
http://kerkermeister.net/how-to-build-an-cocos2d-ios-app-communicating-with-a-restful-api-the-sequence/
The Post contains all the tiny steps you need to follow to get Steve's solution approach with blocks working. That includes:
- an updateable view that will render information as soon as retrieved from Web API asynchronously
- a controller invoking the HTTP request to the Web API
- the actual HttpRequest class that uses iOS standard NSURLConnections
- a model class that uses blocks as callbacks to update its data
Your going to have to either implement a separate method in which you use the data once the data has been returned by the connectionDidFinishLoading method or make the request synchronously. The reason I believe the above does not work is because the request is happening on a separate thread, so the main thread continues, but does not actually have the data.
This is a good way to do that if synchronous is what you want:
Does all NSURLConnections connect asynchronously? iOs
In order to download data from webserivce - use NSURLSession -
A URL session task that returns downloaded data directly to the app in memory.
// 1. create NSURL link to your webservice
NSString *dataUrl = #"DATA_LINK_TO_WEBSERVICE";
NSURL *url = [NSURL URLWithString:dataUrl];
// 2. create a NSURLSessionDataTask
NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession]
dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Handle response here
}];
// 3.resume the task
[downloadTask resume];
Refernces:
apple documentation refrence:
https://developer.apple.com/documentation/foundation/nsurlsessiondatatask?language=objc
Raywanderlich great cookbook:
https://www.raywenderlich.com/2392-cookbook-using-nsurlsession
Your going to need to parse the XML that comes back. There are some good Objective C XML parsers out there. One in particular is made for ease of use....
http://nfarina.com/post/2843708636/a-lightweight-xml-parser-for-ios
It's a very light weight parser for extracting the values you want from XML. I've used many times with great success and little hassle. Here is how I query a web address and turn it into data.
NSString *query = [NSString stringWithFormat:#"http://WEB_ADDRESS_FOR_XML];
NSURL *URL = [NSURL URLWithString:query];
NSData *data = [NSData dataWithContentsOfURL:URL];
Or with NSURLConnection, in the did receive data:
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
//USE THE DATA RETURNED HERE....
}
Then use the Parser from my link to get the contents:
SMXMLDocument *document = [SMXMLDocument documentWithData:data error:NULL];
NSLog("\nXML Returned:%#",document);

Little problem with AsyncUdpSocket receiving data after connecting to broadcast instead of server

I have a problem with AsyncUdpSocket.
I used to connect to a server, send some data and get some response. Now since I do not know the actual address of the server I had to change my code and send the data to the broadcast address 255.255.255.255.
Here is my code :
NSString *bchost = #"255.255.255.255";
NSString *host = #"10.1.0.1";
int udpPort = 6001;
AsyncUdpSocket *udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToPort:udpPort error:nil];
[udpSocket enableBroadcast:YES error:nil];
NSError *error = nil;
if ([udpSocket connectToHost:bchost onPort:udpPort error:&error])
{
[udpSocket receiveWithTimeout:10 tag:0];
[self sendToUDPServer:#"HELLO"];
}
So, the problem is that it works with "host" but not with "bchost". On both cases I see on the server side that the data is received and the answer is sent to the address of the sender (which should be the iOS device) but on the device I do not get the data when I send it to bchost.
Any idea what I am missing ?
Ok, unfortunately all reply's do not work for me but I found the solution, finally ;)
NSString *bcHost = #"255.255.255.255";
NSString *anyHost = #"0.0.0.0";
int UDP_SOCKET_PORT = 6001;
int DISCOVERY_PORT = 6003;
udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToAddress:anyHost port:DISCOVERY_PORT error:nil];
[udpSocket enableBroadcast:YES error:nil];
[udpSocket receiveWithTimeout:10 tag:0];
[udpSocket sendData:[#"Hello" dataUsingEncoding:NSASCIIStringEncoding] toHost:bcHost port:UDP_SOCKET_PORT withTimeout:-1 tag:0];
If there is an server behind it, it will trigger a response and this will also allow to get the ip from the server for further processing.
You are 'connecting' to the host. Per the Unix socket FAQ, this means you'll only get UDP packets back that have a source IP address of 255.255.255.255. Connecting establishes a 1-to-1 relationship for UDP, such that received packets whose source addresses differ from the connected address will be filtered.
If you don't connect (you'll have to change your send line to target the broadcast address), it should work. You'll send toHost:bcHost -- and then your receive should get all packets destined for its port.
Based on Arthur's answer, here's the working code. I'm wondering if receive should start before the send, just to make sure we don't miss a very fast reply before receive is ready, but so far it doesn't seem necessary in my situation. Also, reference this post on how to create receiving methods.
AsyncUdpSocket *udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToPort:1234 error:nil ];
[udpSocket enableBroadcast:YES error:nil];
NSData* data=[messageToSend dataUsingEncoding:NSUTF8StringEncoding];
if ([udpSocket sendData:data toHost:#"255.255.255.255" port:4321 withTimeout:3 tag:0])
{
//2 second timeout. onUdpSocket methods will provide results
[udpSocket receiveWithTimeout:2 tag:0];
}
I could be completely crazy, but it seems like this is a standing issue with AsyncUdpSocket on iOS.
There's several error reports similar and even identical to yours on their Google Code page; people have complained that they are unable to receive Udp packets after broadcasting, and in some cases, even at all.
http://code.google.com/p/cocoaasyncsocket/issues/list?can=2&q=AsyncUdpSocket+receive

writeData on a syncsocket always blocks on iPhone

I use the asyncsocket sample as a starting point to learn more about wlan communication on iPhone.
On the Mac I start a sample server opening port 0. This works, since I can write data with a test client running on the mac.
On the iPhone I think I managed to connect since "streams connected" returns YES.
Then I would like to send data with a syncsocket: (EDITED VERSION WITH COMPLETE CODE)
import "InterfaceTestAppDelegate.h"
import "InterfaceTestViewController.h"
import "AsyncSocket.h"
import "SyncSocket.h"
#implementation InterfaceTestAppDelegate
#synthesize window;
#synthesize viewController;
(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)remoteHost port:(UInt16)remotePort
{
NSLog(#"Socket is connected!");
NSLog(#"Remote Address: %#:%hu", remoteHost, remotePort);
NSString *localHost = [sock localHost];
UInt16 localPort = [sock localPort];
NSLog(#"Local Address: %#:%hu", localHost, localPort);
}
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"application:didFinishLaunchingWithOptions:");
/*
asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
if (![asyncSocket connectToHost: #"192.168.0.30" onPort: 1234 error: &err])
{
NSLog(#"Error connecting: %#", err);
}
NSData *data = [#"testxyz" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"trace 1");
[asyncSocket writeData:data withTimeout:10 tag:0];
NSLog(#"trace 2");
*/
syncSocket = [[SyncSocket alloc] initWithTimeout: 10];
syncSocket.nsLog = YES;
if (![syncSocket connectToHost: #"192.168.0.30" onPort: 12345])
{
NSLog(#"Error connecting syncSocket:");
}
NSData *data = [#"testxyz" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"syncSocket trace 1");
[syncSocket writeData:data];
NSLog(#"syncSocket trace 2");
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
It never continues to send the data, the writeData always blocks.
The IP 192.168.0.30 is my Mac's IP. I just used any port 12345 now as you suggested above.
But I don't really know what I have to do on the Mac to receive??
As you can see I actually use syncsocket, then it blocks.
I also tried asyncSocket, then I get the message in the asyncsocket class: writeStream Can NOT Accept Bytes
Maybe its that I don't setup the Mac correctly,ie what app do I need to run on the Mac to test?
Many thank!
For what it's worth, this is specifically how you typically read in some data using AsyncSocket:
-(void)onSocket:(AsyncSocket *)sock
didReadData:(NSData*)data withTag:(long)tag
{
[data getBytes:&getMe length:sizeof(CommProt)];
// now, you must roll in the next read...
[sock readDataToLength:sizeof(CommProt) withTimeout:-1 tag:0];
// CommProt is your communications protocol, so sizeof(CommProt)
// is how much to read at a chunk.
// you can now simply access the fields of getMe,
// for example getMe.x, getMe.y, getMe.latestValue etc etc.
// hope it helps!
}
Of course, you would have previously rolled in the first "primer" read command:
You do that when you connect to a host, hence:
-(void)onSocket:(AsyncSocket *)sock
didConnectToHost:(NSString *)host port:(UInt16)port
{
if ( yourAppSaysItsOkToConnectAtThisMoment == NO )
{
[sock disconnect]; // (so easy, AsyncSockets is a masterpiece)
return;
}
// .. blah blah
// the critical 'primer' read command
[sock readDataToLength:sizeof(CommProt) withTimeout:-1 tag:0];
// .. blah blah
}
Don't forget you must roll in the next read in two places, (a) when you first connect and (b) of course, after each read!
In the example your communications protocol would look like this ...
typedef struct _CommProt // v.3
{
BOOL pressExplosionButton;
BOOL pressFireworksButton;
float usersSteering;
float usersTemperature;
float usersAltitude;
float usersAngle;
}
CommProt;
Variable like "getMe" in the example would simply look like this:
CommProt getMe;
CommProt sendMe;
If you are struggling to understand this type of communications protocol, also try this long answer:
Tablet(iPad/Android)-Server Communication Protocol
AsyncSocket is incredibly beautiful, it was written by the mysterious Justin Voss who seemed to drop off the internet after giving it to the world - it's one of the best libraries ever written, it's a masterpiece.
Hope it helps.

SSDP on the iPhone

I need to be able to send out a UDP message and also receive one in order to discover SSDP devices on the network from the iPhone.
I know that I need to send the packet to the multicast address and my HTTP request needs to look something like this:
M-SEARCH * HTTP/1.1
Host: 239.255.255.250:1900
Man: ssdp:discover
Mx: 3
ST: "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
From reading the docs it appears that I can do all this with CFNetwork and despite reading (and re-reading the docs) I am struggling to get started. Can anyone recommend and tutorials or code snippets to get me over the initial learning hump?
I've got the CFNetwork programming guide:
http://developer.apple.com/mac/library/documentation/Networking/Conceptual/CFNetwork/CFNetwork.pdf
and Beej's Guide to Network programming Using Internet Sockets:
http://beej.us/guide/bgnet/
Thanks
Dave
P.S.
I am unable to use any of the 3rd party libraries and frameworks in this instance.
I have used AsyncUdpSocket successfully to run SSDP Discovery and find controllers. Here are my code snippets:
Initialize and setup the socket:
// AsyncUdpSocket *ssdpSock = [[AsyncUdpSocket alloc] initWithDelegate:self];
AsyncUdpSocket *ssdpSock = [[AsyncUdpSocket alloc] initIPv4];
[ssdpSock setDelegate:self];
Note the first line commented out. I found on the AsyncUdpSocket forums some issues with duplicates. I don't think I was facing them but I did it anyhow.
I added error checking, and it was useful because during my debugging I wasn't closing sockets and I started getting socket setup failures:
NSError *socketError = nil;
if (![ssdpSock bindToPort:1900 error:&socketError]) {
NSLog(#"Failed binding socket: %#", [socketError localizedDescription]);
return statusController;
}
if(![ssdpSock joinMulticastGroup:#"239.255.255.250" error:&socketError]){
NSLog(#"Failed joining multicast group: %#", [socketError localizedDescription]);
return statusController;
}
if (![ssdpSock enableBroadcast:TRUE error:&socketError]){
NSLog(#"Failed enabling broadcast: %#", [socketError localizedDescription]);
return statusController;
}
[ssdpSock sendData:[self.discoverControllerString dataUsingEncoding:NSUTF8StringEncoding]
toHost:#"239.255.255.250"
port:1900
withTimeout:2
tag:1];
Notice the changes I have made to the time out. And then finally did the receive setup, and closed the socket. Note the socket close. Since I am in my own class when I am running this - the code above did not work for me.
[ssdpSock receiveWithTimeout: 2 tag:1];
[NSTimer scheduledTimerWithTimeInterval: 5 target: self
selector:#selector(completeSearch:) userInfo: self repeats: NO];
[ssdpSock closeAfterSendingAndReceiving];
The most important change probably was returning "NO" if I did not find my controller. The first receive was incidentally the discovery message itself coming back. And when I read through the AsyncUdpSocket.h file carefully - returning "NO" when it is not a packet you are looking for helped.
Also note that I am using ARC in my code but I compiled the AsyncUdpSocket without ARC support.
-(void) completeSearch: (NSTimer *)t
{
NSLog(#"%s",__FUNCTION__);
//[ssdpSock close];
//ssdpSock = nil;
}
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock
didReceiveData:(NSData *)data
withTag:(long)tag
fromHost:(NSString *)host
port:(UInt16)port
{
NSLog(#"%s %ld %# %d",__FUNCTION__,tag,host,port);
NSString *aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%#",aStr);
NSString *compareString = [aStr stringByPaddingToLength:[self.responseString length] withString:#"." startingAtIndex:0];
//NSLog(#"%#", compareString);
//NSLog(#"%#", self.responseString);
if ([compareString isEqualToString:self.responseString])
{
NSLog(#"String Compare, Controller Found!");
[self.controllerList addObject:aStr];
//NSData *controllerIP = [aStr dataUsingEncoding:NSUTF8StringEncoding];
[[NSNotificationCenter defaultCenter] postNotificationName:#"DiscoveredController" object:nil];
return YES;
}
return NO;
}
I have the following code for SSDP search in my app:
-(void)discoverDevices {
ssdpSock = [[AsyncUdpSocket alloc] initWithDelegate:self];
[ssdpSock enableBroadcast:TRUE error:nil];
NSString *str = #"M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMan: \"ssdp:discover\"\r\nST: mydev\r\n\r\n";
[ssdpSock bindToPort:0 error:nil];
[ssdpSock joinMulticastGroup:#"239.255.255.250" error:nil];
[ssdpSock sendData:[str dataUsingEncoding:NSUTF8StringEncoding]
toHost: #"239.255.255.250" port: 1900 withTimeout:-1 tag:1];
[ssdpSock receiveWithTimeout: -1 tag:1];
[NSTimer scheduledTimerWithTimeInterval: 5 target: self
selector:#selector(completeSearch:) userInfo: self repeats: NO]; }
-(void) completeSearch: (NSTimer *)t {
NSLog(#"%s",__FUNCTION__);
[ssdpSock close];
ssdpSock = nil;}
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
NSLog(#"%s %d %# %d",__FUNCTION__,tag,host,port);
NSString *aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%#",aStr);}
It uses the AsyncUdpSocket from CocoaAsyncSocket.
OK, finally done it. Found a class in the public domain (thanks Chris) called AsyncUdpSocket that lets you create a UDP socket which you can then turn on broadcasting and join the multicast address.
There is a nice sendData method, complete with adding to a run loop to prevent blocking.
Hope that helps.
Dave