PDA

View Full Version : CocoaAsyncSocket not working




intox
Dec 18, 2011, 11:08 AM
Im trying to translate a php script to work natively with our cocoa application.

I am using CocoaAsyncSocket to handle UDP connections.

In php its done..
$fp = fsockopen("udp://".$set['IP'],$set['Port'], $errno, $errstr, 2);
socket_set_timeout($fp,2);
$query = "\xFF\xFFcmd $authtok $goget";
fwrite($fp,$query);
$d = fread ($fp, 10000);
And I am doing..

socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSString *msg = [NSString stringWithFormat:@"%c%ccmd %@ %@", 0xFF, 0xFF, authtok, goget];
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
[socket sendData:data toHost:@"85.25.248.160" port:28960 withTimeout:-1 tag:tag];

NSLog(@"%@", FORMAT(@"SENT (%i): %@", (int)tag, msg));
Logged is..

ˇˇcmd <authtok> <goget>
Which is as expected, but nothing happens on the server and no response is given.

Cheers.



intox
Dec 18, 2011, 03:51 PM
Does anybody have any idea how to do this?

I am not bothered about using CocoaAsyncSocket if it can be done another way, easier.

From what I can tell, which is not a huge amount, is that when I add 0xFF to the string, its no longer hex, and sent wrong.

jiminaus
Dec 18, 2011, 04:05 PM
You haven't sent connectToHost:port:error: to socket.

See Intro GCDAsyncSocket (https://github.com/robbiehanson/CocoaAsyncSocket/wiki/Intro_GCDAsyncSocket).


Also the code you have will not send FFFF to the server. It will end up sending CB87CB87 because your code encodes FFFF into UTF-8. What you want to do is only only encode the cmd string as UTF-8 (msg without the FFFF prefix). Create an NSMutableData object, append FFFF to the mutable data object, and then append UTF-8 encoding of the cmd string.

intox
Dec 18, 2011, 05:58 PM
Thanks for the post, I did call the connect, just forgot to add it to the code, after some help from #iphonedev I managed to get it working:

socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

[socket connectToHost:ip onPort:port error:nil];

char *bytes = "\xff\xffcmd command variable";
NSData* data = [[NSData alloc] initWithBytes:bytes length:strlen(bytes)];
[socket sendData:data withTimeout:-1 tag:0];

I guess the the string, being encoded by UTF-8 messed with the \xFF.

Now to the next problem, as you can see the delegate is set to self, and didSendDataWithTag: is called.

But didReceiveData: is not, do I need to call it myself - shouldn't it be called? As the server is responding.

You haven't sent
connectToHost:port:error: to socket.

See Intro GCDAsyncSocket (https://github.com/robbiehanson/CocoaAsyncSocket/wiki/Intro_GCDAsyncSocket).


Also the code you have will not send FFFF to the server. It will end up sending CB87CB87 because your code encodes FFFF into UTF-8. What you want to do is only only encode the cmd string as UTF-8 (msg without the FFFF prefix). Create an NSMutableData object, append FFFF to the mutable data object, and then append UTF-8 encoding of the cmd string.

chown33
Dec 18, 2011, 06:24 PM
Post your code that does the receiving.

Did you call beginReceiving: ? Did you call it before sending? If you don't, then you have a race condition with the server's reply.

intox
Dec 18, 2011, 06:32 PM
There is not much code at the moment, do I need to call something myself to receive a response?

socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

[socket connectToHost:ip onPort:port error:nil];
[socket beginReceiving:error];

char *bytes = "\xff\xffcmd command variable";
NSData* data = [[NSData alloc] initWithBytes:bytes length:strlen(bytes)];
[socket sendData:data withTimeout:-1 tag:0];

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext {
NSLog(@"Working");
}

Working is never logged.

Post your code that does the receiving.

Did you call beginReceiving: ? Did you call it before sending? If you don't, then you have a race condition with the server's reply.

chown33
Dec 18, 2011, 07:27 PM
...
[socket beginReceiving:error];
...
}

Show the declaration of the 'error' variable. My first guess is you're doing it wrong, but without seeing actual declarations, it's just a guess. I have the feeling you're just slapping code together without first thinking it through.


Are you spending any time at all reading the docs for the methods you're calling? Do you think you should do some error-checking, such as the return value from beginReceiving: and/or the referenced NSError?

Have you looked at any examples? Searched with google for examples? Searched for examples that use the older AsyncUdpSocket?

Show the code that comes after you send your datagram. Does it have a runloop, or does it exit shortly thereafter?

What are you doing to test and debug this yourself? Have you thought about using the 'tcpdump' command to sniff packets on the wire, and see what's actually being sent and received? How about the 'nc' command (netcat), which lets you send and receive UDP datagrams using simple command lines?

intox
Dec 18, 2011, 08:09 PM
NSError *error = nil;
[socket beginReceiving:&error];

Sorry - I added in beginReceiving: through post reply.

I also did the following, which did not fail. error said nothing either

if(![socket beginReceiving:&error]) {
NSLog(@"no working");
}

I have searched for examples, in fact spent most of the day going through examples, mostly from the problem I had first, though I shall continue too and re-read them.

There is no code other than logging after I send.

To test, so far I have done most of the debugging on my server, which successfully receives the packets, correctly, and responds with the correct response.

Sorry for my ignorance, I am not very experienced at all when it comes to networking, I shall look into the tools you mentioned.

Thanks for your patience and advice.

Update: I have run tcpdump, and found the following entries..

03:13:52.876484 IP 192.168.2.36.60524 > ipadrs.28960: UDP, length 25
03:13:52.969327 IP ipadrs.28960 > 192.168.2.36.60524: UDP, length 240


I presume that is the packet sent and its response?

Show the declaration of the 'error' variable. My first guess is you're doing it wrong, but without seeing actual declarations, it's just a guess. I have the feeling you're just slapping code together without first thinking it through.


Are you spending any time at all reading the docs for the methods you're calling? Do you think you should do some error-checking, such as the return value from beginReceiving: and/or the referenced NSError?

Have you looked at any examples? Searched with google for examples? Searched for examples that use the older AsyncUdpSocket?

Show the code that comes after you send your datagram. Does it have a runloop, or does it exit shortly thereafter?

What are you doing to test and debug this yourself? Have you thought about using the 'tcpdump' command to sniff packets on the wire, and see what's actually being sent and received? How about the 'nc' command (netcat), which lets you send and receive UDP datagrams using simple command lines?

chown33
Dec 18, 2011, 09:30 PM
Update: I have run tcpdump, and found the following entries..

[CODE]03:13:52.876484 IP 192.168.2.36.60524 > ipadrs.28960: UDP, length 25
03:13:52.969327 IP ipadrs.28960 > 192.168.2.36.60524: UDP, length 240


I presume that is the packet sent and its response?

Looks like it to me.

Questions you should ask yourself:
1. What port are you receiving on? Does this port match the one that the reply from the server is being sent on?
2. Is the receiving program running long enough that a reply directed to the proper port will actually be seen by that program? (This is why I asked about a runloop that comes after the send.)

If you don't have experience with UDP, you might want to review how it works, to make sure you're doing the right thing.

It's been a few years since I messed around with AsyncUDPSocket. It worked first time for me. But I did set it up with a runloop, and knew what to expect when sending and receiving packets.

jiminaus
Dec 18, 2011, 09:41 PM
Nevermind. I'm surprised how rusty I am at the mechanics of an IP stack.

intox
Dec 19, 2011, 08:35 AM
Where do I check what ports I am receiving on? The server replies the response using the same port I send to the server. (e.g. 60524 in the log above).

I am going to look into a runloop, though I do not end anything myself.

From what I've seen browsing the web is that it works for everybody the first time.

I have checked

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag;

[sock connectedHost] returns the connected host, tag is the correct one too.

Looks like it to me.

Questions you should ask yourself:
1. What port are you receiving on? Does this port match the one that the reply from the server is being sent on?
2. Is the receiving program running long enough that a reply directed to the proper port will actually be seen by that program? (This is why I asked about a runloop that comes after the send.)

If you don't have experience with UDP, you might want to review how it works, to make sure you're doing the right thing.

It's been a few years since I messed around with AsyncUDPSocket. It worked first time for me. But I did set it up with a runloop, and knew what to expect when sending and receiving packets.

chown33
Dec 19, 2011, 09:56 AM
Is your program a command-line tool, or a double-clickable application? What Xcode template did you use? If you don't know what a runloop is, you might already have one.

Consider posting the complete project, with all source, as a zip-file somewhere (like pastebin.com), and post its URL here. You haven't yet posted enough surrounding code to diagnose anything. The behavior of the program depends on all its code, not just isolated fragments.

intox
Dec 19, 2011, 10:33 AM
The project is a new Xcode Cocoa Application, Pretty much everything is done in the applicationDidFinishLaunching.

Code: http://pastebin.com/mUvsqD4B

Like I said its a new project all I have other than that is the latest CocoaAsyncSocket, no UI or anything else yet.

Is your program a command-line tool, or a double-clickable application? What Xcode template did you use? If you don't know what a runloop is, you might already have one.

Consider posting the complete project, with all source, as a zip-file somewhere (like pastebin.com), and post its URL here. You haven't yet posted enough surrounding code to diagnose anything. The behavior of the program depends on all its code, not just isolated fragments.

chown33
Dec 19, 2011, 06:03 PM
I suggest moving everything dealing with UDP out of applicationDidFinishLaunching. Instead, put it in an IBAction method.

Then make a GUI with a single pushbutton. Connect that button to the IBAction method.

The result should be that pushing the button causes the send, and presumably a subsequent receive of the server's reply.

Make sure you're managing objects correctly. For example, don't release the GCDAsyncUdpSocket object until after the reply datagram arrives. Or only use a singleton GCDAsyncUdpSocket object, created and connected only once. That means reusing a single GCDAsyncUdpSocket for multiple sends and receives, and knowing enough about its state that you're using it correctly.

Simplify.

Eliminate as much unnecessary cruft as possible. The app-delegate code you put on pastebin has so much extra junk it's difficult to see where things might be going wrong.

The first app should have one purpose: to test GCDAsyncUdpSocket in as simple a way as possible. It should have one action trigger (pushbutton), with exactly one action that happens. If you get it working, then you can create another app with more features later.

If you know the port the app is receiving on, you should be able to use netcat (the 'nc' command) to send datagrams to it.

intox
Dec 20, 2011, 02:07 AM
I have put everything into an IBAction like you said and executed it from a button - sends fine, but still no response.

I am currently not releasing the object, tried removing that a while ago.

I've commented out pretty much everything in the app delegate that I am not using to try and do this with, currently the only things not commented are the is applicationDidFinishLaunching: and my new IBAction.

I will look into netcat now, but it seems the sending port changes every time I send something.

08:37:15.723086 IP 192.168.2.36.62359 > <remoteip>.28960: UDP, length 25
08:37:15.822720 IP <remoteip>.28960 > 192.168.2.36.62359: UDP, length 240

// 2 seconds later

08:37:17.972370 IP 192.168.2.36.59363 > <remoteip>.28960: UDP, length 25
08:37:18.059126 IP <remoteip>.28960 > 192.168.2.36.59363: UDP, length 240


Update: I just verified that everything is working, with the server.. the following responds the expected response into terminal.

echo -e "\xFF\xFFcmd command variable" | nc -u <remoteip> 28960

I suggest moving everything dealing with UDP out of applicationDidFinishLaunching. Instead, put it in an IBAction method.

Then make a GUI with a single pushbutton. Connect that button to the IBAction method.

The result should be that pushing the button causes the send, and presumably a subsequent receive of the server's reply.

Make sure you're managing objects correctly. For example, don't release the GCDAsyncUdpSocket object until after the reply datagram arrives. Or only use a singleton GCDAsyncUdpSocket object, created and connected only once. That means reusing a single GCDAsyncUdpSocket for multiple sends and receives, and knowing enough about its state that you're using it correctly.

Simplify.

Eliminate as much unnecessary cruft as possible. The app-delegate code you put on pastebin has so much extra junk it's difficult to see where things might be going wrong.

The first app should have one purpose: to test GCDAsyncUdpSocket in as simple a way as possible. It should have one action trigger (pushbutton), with exactly one action that happens. If you get it working, then you can create another app with more features later.

If you know the port the app is receiving on, you should be able to use netcat (the 'nc' command) to send datagrams to it.

chown33
Dec 20, 2011, 09:50 AM
Glad you got it working.

For the benefit of future readers who find this thread, and for my own curiosity, could you describe what turned out to be the problem, and how you solved it.

intox
Dec 20, 2011, 12:08 PM
Oh, I didn't mean I had gotten the response through my application, I just meant I could get the successful response, through terminal when sending the exact same data.

Glad you got it working.

For the benefit of future readers who find this thread, and for my own curiosity, could you describe what turned out to be the problem, and how you solved it.

chown33
Dec 20, 2011, 12:32 PM
Zip up the complete project, with refactored code and one-button GUI, and put it on pastebin or some other location.

Or if it's small enough, attach it to a post here on MacRumors.

General suggestions:
1. Make sure your delegate method is spelled correctly. The protocol's methods are all optional, so there's no compile-time checking that your method matches the protocol's method. Consider changing the protocol header to omit @optional.

2. Go into the source of GCDAsyncUdpSocket and look for the point right before it calls the did-receive delegate method. Put either a breakpoint or an NSLog there. If nothing shows up, move earlier in the receive handling, until you get something happening on receive.

3. Try using the older non-GCD AsyncUdpSocket. You'll have to change some of your code, but the overall design is similar enough it shouldn't be a big obstacle.


p.s. It's not necessary to quote my entire post in your replies.