I am using GCDAsyncSocket in my application. I have a few problems.
When the app goes to background, I like to close the socket. During Suspend applicationDidEnterBackground call, I execute the following code but it doesn't disconnect the socket. (I check this via connected host)
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket setDelegate:self];
Once I standby the phone, despite my call, the socket is not closed. After 60 seconds iOS closes the socket since we are at background and things go back to normal but the sender lost 60 seconds of data. I am probably not using API correctly but it could also be an issue.
Rest of my code below for reference:
Initialization
dispatch_queue_t socketQ = dispatch_queue_create("socketQueue", NULL);
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQ];
if(![asyncSocket connectedPort])
{
if (![asyncSocket acceptOnPort:WIFI_BOX_PORT error:&error])
{
NSLog(#"Problem with opening Port: %#", error);
}
else
{
NSLog(#"Port opened, waiting for connection");
}
}
Related
I am using GCDAsyncSocket for connecting to socket. App works fine till phone gets lock.
When phone gets unlock then socketDidDisconnect gets call with error (Socket closed by remote peer). there I am reconnecting to server but socket gets disconnected every time. Is there any way to reconnect to socket?
Here is my Code :
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
NSLog(#"Socket Disconnected===== %#",err);
[self serverConnection];
}
-(void)serverConnection
{
asyncSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *err = nil;
if (![asyncSocket connectToHost:ipAddress onPort:portNumber error:&err]){
NSLog(#"Error in acceptOnPort:error: -> %#", err);
}
else
{
NSLog(#"Socket Connecting");
}
}
I am making an app to send UDP packets in order to switch on a LED bulb. I have been able to perform all the actions when I am connecting to the Ad-hoc created by the Wifi bridge.
Now, I want to configure the Wifi bridge so that it can connect to my main router. I have the AT command set to perform this procedure but somehow I am not able to receive the response form the Wifi bridge for the commands which I am sending to it.
The procedure is as follows:-
Step 1 : Send UDP message to the LAN broadcast IP address of "10.10.100.255" and port of 48899 => "Link_Wi-Fi"
All Wifi bridges on the LAN will respond with their details. Response is "10.10.100.254, ACCF232483E8"
Step 2 : (optional for changing settings on the wifi bridge): Then send "+ok" to the LimitlessLED Wifi Bridge. Send UDP message to the response IP address returned from step 1 "10.10.100.254" => "+ok"
Step 3 : (optional for changing settings on the wifi bridge): After that you may send AT commands (ending with \r\n) to the module.
The code for sending the UDP packets is as follows
-(void)configureWifi{
counter++;
NSString *host = #"10.10.100.255";
if ([host length] == 0)
{
[self logError:#"Address required"];
return;
}
int port = 48899; //[portField.text intValue];
if (port <= 0 || port > 65535)
{
[self logError:#"Valid port required"];
return;
}
NSString *msg = #"Link_Wi-Fi";
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"the message sent is %#", data);
[udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:tag];
}
Now in order to setup the socket and to receive the data I am using these two delegate methods:
- (void)setupSocket
{
// Setup our socket.
// The socket will invoke our delegate methods using the usual delegate paradigm.
// However, it will invoke the delegate methods on a specified GCD delegate dispatch queue.
//
// Now we can configure the delegate dispatch queues however we want.
// We could simply use the main dispatc queue, so the delegate methods are invoked on the main thread.
// Or we could use a dedicated dispatch queue, which could be helpful if we were doing a lot of processing.
//
// The best approach for your application will depend upon convenience, requirements and performance.
//
// For this simple example, we're just going to use the main thread.
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![udpSocket bindToPort:0 error:&error])
{
[self logError:FORMAT(#"Error binding: %#", error)];
return;
}
if (![udpSocket beginReceiving:&error])
{
[self logError:FORMAT(#"Error receiving: %#", error)];
return;
}
[self logInfo:#"Ready"];
}
and to Receive data this is the method which is note getting called after sending the UDP packets. This is the delegate method of the GCDAsyncUdpSocket class which I have used in my project in order to send and receive the UDP packets.
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
[self logMessage:FORMAT(#"RECV: %#", msg)];
}
else
{
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
[self logInfo:FORMAT(#"RECV: Unknown message from: %#:%hu", host, port)];
}
}
Once I am able to receive the response I will be able to send the next AT commands in order to configure the Bridge.
Thanks. Any help will be appreciated.
Here are the troubleshooting steps I recommend that you use :
1- I'm assuming you are using ARC so make sure that your udpSocket variable has a strong reference throughout the asynchronous communication. If it is being freed, then that could explain the absence of a callback.
2- Make sure the communication is really happening the way you think it is. Use a software such as Wireshark to capture the packets being exchanged on the network. This should allow you to confirm that your packets do get sent upon calling sendData: and it will also allow you to confirm whether or not you are getting a reply back.
3- Make sure you are using the GCDAsyncUdpSocket properly. Considering you want to broadcast a message, you shouldn't be calling bindToPort:error: in your setupSocket method. Instead you should be calling enableBroadcast:error:. Considering you also want to receive packets after broadcasting, you should use the connectToHost:onPort:error: method to change the state of the socket to allow for bidirectional communication. After that is done, you can replace your usage of sendData:toHost:port:withTimeout:tag: by sendData:withTimeout:tag:. Finally, you can call beginReceiving: so that the delegate gets called for any incoming packets.
4- If this still doesn't get you through it, I recommend that you read throughly the documentation of the GCDAsyncUdpSocket which is very well documented.
You can trouble shoot the problem using Wireshark or any network capture tool.
We use to work in similar kind of project where we used Wireshark extensively.
If packet has reached device(Z-Wave ) it will send out some sort of Ack.
this will help to make sure packets are getting out.
I am working on an iPhone app for controlling physical hardware.
The routine is as follows:
app sends a specific 8-bytes "wake up" datagram on the broadcast channel, on port 8089; the message is repeatedly sent.
external hardware, which is listening to port 8089, receives the message and sends a 94 bytes datagram containing, among other stuff, the IP and MAC address of the hardware; this is also on the broadcast channel.
app stops sending the "wake up" message, stores IP, and begins communicating with the hardware via a TCP socket
The routine generally works. However, I often get an inexplicable loss of UDP packages on reception; that is, I send the 8-bytes "wake up" signal, but I don't get the 94 bytes response.
When the app works, it works perfectly: I hardly lose a single package, and if the app misses the first 94-bytes message, it gets the second or third one.
When it doesn't work, it misses all packages, constantly.
The "not-working" stage might last for minutes, or hours; I've found no apparent trigger - it's as if at some stage, with no reason, reception stops working.
Before asking here, I've done very extensive debugging. I've monitored the sockets via rvictl and tcpdump, and confirmed that my logs reflect what happens at the socket level.
To take the external hardware out of the equation, I've built a mirror app that behaves as the hardware should. I've tried changing ports, I tried to close and nil sockets all times, to reset them , to pause and restart reception. None of this worked.
I've developed my communication library with GCDAsyncUdpSocket. To be on the safe side, I've experimented also with the non GCD version; the result is the same. I built my own minimal wrapper around C sockets; I got the same behaviour.
Here is my implementation code:
GCDAsyncUdpSocket *udpSocket;
- (void)startUdpBroadcast {
if (udpSocket == nil)
{
// Setup our socket.
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[udpSocket setIPv6Enabled:NO];
}
NSLog(#"Listening for a message on %#",[self getBroadcastAddress]);
NSError *bindError = nil;
NSError *enableError = nil;
NSError *receivingError = nil;
[udpSocket bindToPort:8089 error:&bindError];
if (bindError) {
NSLog(#"Error, can't bind: %#",[bindError localizedDescription]);
return;
}
[udpSocket enableBroadcast:YES error:&enableError];
if (enableError) {
NSLog(#"Error, can't enable broadcast: %#",[enableError localizedDescription]);
return;
}
if (![udpSocket beginReceiving:&receivingError])
{
NSLog(#"Error, can't receive: %#",[receivingError localizedDescription]);
return;
}
[self logInfo:#"Start listening to UDP boradcast channel"];
[self sendUdpSignal];
repeatedBroadcastTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(sendUdpSignal) userInfo:nil repeats:YES];
}
-(void)sendUdpSignal {
NSData* signal = [self udpBroadcastSignal] ;
NSLog(#"Send broadcast signal %# on %#",signal, [self currentNetworkSsid]);
[udpSocket sendData:signal toHost:[self getBroadcastAddress] port:8089 withTimeout:10 tag:0];
}
Do you have any suggestion?
Is there a way to make sure socket reception is never stopped?
Thanks in advance,
Davide
I am using GCDAsyncUdpSocket to write a UDP socket in my app. The scenario is like this: when users click the button, it will send a broadcast packet in LAN then listen to the response, there is a server in LAN which will respond with one UDP packet. When the app receives the response, it will do something.
I set GCDAsyncUdpSocket as followings:
- (void)setupSocket
{
_udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![_udpSocket bindToPort:18686 error:&error]) {
NSLog(#"Error binding: %#",error);
return;
}
if (![_udpSocket beginReceiving:&error]) {
NSLog(#"Error receiving: %#",error);
return;
}
if (![_udpSocket enableBroadcast:YES error:&error]) {
NSLog(#"Error enableBroadcast: %#",error);
return;
}
}
then i send Packet in button action as following:
NSString *host = #"255.255.255.255";
int port = 8585;
NSString *msg = #"Hello from iOS";
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
[_udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:0];
in
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
method i listen the port to do somethings. It works perfectly at beginning, but if you try to click the button later (about 1 hour), then it cannot send UDP packet any more.
My server in LAN will print the data received. I thought there was something wrong with send method. so i use BSD socket methods to send The data. and use GCDAsyncUdpSocket to receive the response. but the same thing happened after a while. this time i can send but cannot receive.
Am i missing something about GCDAsyncUdpSocket? why it cannot send/receive after a while? Any help would be much appreciated.
It may be some timeout setting. Implement the GCDAsyncUdpSocketDelegate protocol to fetch detailed information about what is going on.
The hard solution is to establish a new connection.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
My application times out while trying to connect to host. The timeout time is set to unlimited so I take it that the client is really just unable to connect at all.
I have an iPad app running asyncsockets and I'm trying to get it to connect to a server on my desktop also using asyncsockets. The iPad is specifically iOS 5 and is using GCD asyncsockets.
The server is being invoked through a NSRunLoop. It receives no form of connection from the client (none of breakpoints are caught like they are for telnet connections).
I'm able to telnet into the server from other machines just fine. I'm also able to connect the iPad client to host:google.com on port:80 just fine.
I've tried connecting the iPad to the server on ports 8080, 4500, and 50000 all to no success (they all work for telnet though).
I believe there is something in the server code causing this but I am not sure.
My server code is from a sample found here: http://mysterycoconut.com/blog/2010/07/tweak-away/
My client code is modified HTTP client code from the sample GCD code supplied by the asyncsockets repository: https://github.com/robbiehanson/CocoaAsyncSocket/blob/master/Examples/GCD/SimpleHTTPClient/Mobile/SimpleHTTPClient/SimpleHTTPClientAppDelegate.m
Here is my server code:
- (id) init;
{
self = [super init];
if (self != nil)
{
debugServer = [[AsyncSocket alloc] initWithDelegate:self];
connectedClients = [[NSMutableArray alloc] initWithCapacity:1];
running = false;
}
return self;
}
- (void) dealloc;
{
[self stop];
[connectedClients release];
[debugServer release];
[super dealloc];
}
- (void) startOnPort
{
if (running) return;
if (_port < 0 || _port > 65535)
_port = 0;
NSError *error = nil;
if (![debugServer acceptOnPort:_port error:&error])
return;
NSLog(#"My Awesome Debug Server has started on port %hu", [debugServer localPort]);
running = true;
}
- (void) stop;
{
if (!running) return;
[debugServer disconnect];
for (AsyncSocket* socket in connectedClients)
[socket disconnect];
running = false;
}
- (void) setPort:(int)in_port{
_port = in_port;
}
- (void)onSocket:(AsyncSocket *)socket didAcceptNewSocket:(AsyncSocket *)newSocket;
{
[connectedClients addObject:newSocket];
}
- (void)onSocketDidDisconnect:(AsyncSocket *)socket;
{
[connectedClients removeObject:socket];
}
- (void)onSocket:(AsyncSocket *)socket didConnectToHost:(NSString *)host port:(UInt16)port;
{
NSLog(#"Accepted client %#:%hu", host, port);
NSData *welcomeData = [#"Welcome to my Awesome Debug Server\r\n\r\n"
dataUsingEncoding:NSUTF8StringEncoding];
[socket writeData:welcomeData withTimeout:-1 tag:WelcomeMsgTag];
[socket readDataWithTimeout:-1 tag:GenericMsgTag];
}
- (void)onSocket:(AsyncSocket *)socket didReadData:(NSData *)data withTag:(long)tag;
{
NSString *tmp = [NSString stringWithUTF8String:[data bytes]];
NSString *input = [tmp stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",input);
if ([input isEqualToString:#"exit"])
{
NSData *byeData = [#"Bye!\r\n" dataUsingEncoding:NSUTF8StringEncoding];
[socket writeData:byeData withTimeout:-1 tag:GenericMsgTag];
[socket disconnectAfterWriting];
return;
}
[socket readDataWithTimeout:-1 tag:GenericMsgTag];
}
#end
...and here is my client code:
- (id) init
{
if (self = [super init]) {
// AsyncSocket optionally uses the Lumberjack logging framework.
//
// Lumberjack is a professional logging framework. It's extremely fast and flexible.
// It also uses GCD, making it a great fit for GCDAsyncSocket.
//
// As mentioned earlier, enabling logging in GCDAsyncSocket is entirely optional.
// Doing so simply helps give you a deeper understanding of the inner workings of the library (if you care).
// You can do so at the top of GCDAsyncSocket.m,
// where you can also control things such as the log level,
// and whether or not logging should be asynchronous (helps to improve speed, and
// perfect for reducing interference with those pesky timing bugs in your code).
//
// There is a massive amount of documentation on the Lumberjack project page:
// https://github.com/CocoaLumberjack/CocoaLumberjack
//
// But this one line is all you need to instruct Lumberjack to spit out log statements to the Xcode console.
[DDLog addLogger:[DDTTYLogger sharedInstance]];
// Create our GCDAsyncSocket instance.
//
// Notice that we give it the normal delegate AND a delegate queue.
// The socket will do all of its operations in a background queue,
// and you can tell it which thread/queue to invoke your delegate on.
// In this case, we're just saying invoke us on the main thread.
// But you can see how trivial it would be to create your own queue,
// and parallelize your networking processing code by having your
// delegate methods invoked and run on background queues.
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
// Now we tell the ASYNCHRONOUS socket to connect.
//
// Recall that GCDAsyncSocket is ... asynchronous.
// This means when you tell the socket to connect, it will do so ... asynchronously.
// After all, do you want your main thread to block on a slow network connection?
//
// So what's with the BOOL return value, and error pointer?
// These are for early detection of obvious problems, such as:
//
// - The socket is already connected.
// - You passed in an invalid parameter.
// - The socket isn't configured properly.
//
// The error message might be something like "Attempting to connect without a delegate. Set a delegate first."
//
// When the asynchronous sockets connects, it will invoke the socket:didConnectToHost:port: delegate method.
NSError *error = nil;
#if USE_SECURE_CONNECTION
uint16_t port = 443; // HTTPS
#else
uint16_t port = 8080; // HTTP
#endif
DDLogVerbose(#"port: %d\t host: %#",port,#"130.85.92.12");
if (![asyncSocket connectToHost:#"130.85.92.12" onPort:port error:&error])
{
DDLogError(#"Unable to connect to due to invalid configuration: %#", error);
}
else
{
DDLogVerbose(#"Connecting...");
}
#if USE_SECURE_CONNECTION
// The connect method above is asynchronous.
// At this point, the connection has been initiated, but hasn't completed.
// When the connection is establish, our socket:didConnectToHost:port: delegate method will be invoked.
//
// Now, for a secure connection we have to connect to the HTTPS server running on port 443.
// The SSL/TLS protocol runs atop TCP, so after the connection is established we want to start the TLS handshake.
//
// We already know this is what we want to do.
// Wouldn't it be convenient if we could tell the socket to queue the security upgrade now instead of waiting?
// Well in fact you can! This is part of the queued architecture of AsyncSocket.
//
// After the connection has been established, AsyncSocket will look in it's queue for the next task.
// There it will find, dequeue and execute our request to start the TLS security protocol.
//
// The options passed to the startTLS method are fully documented in the GCDAsyncSocket header file.
// The deusty server only has a development (self-signed) X.509 certificate.
// So we tell it not to attempt to validate the cert (cause if it did it would fail).
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]
forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
[asyncSocket startTLS:options];
#endif
}
return self;
}
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
DDLogVerbose(#"socket:didConnectToHost:%# port:%hu", host, port);
// HTTP is a really simple protocol.
//
// If you don't already know all about it, this is one of the best resources I know (short and sweet):
// http://www.jmarshall.com/easy/http/
//
// We're just going to tell the server to send us the metadata (essentially) about a particular resource.
// The server will send an http response, and then immediately close the connection.
NSString *msg = #"iOS client connected\r\n\r\n";
NSData *msgdata = [msg dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:msgdata withTimeout:-1.0 tag:0];
// Side Note:
//
// The AsyncSocket family supports queued reads and writes.
//
// This means that you don't have to wait for the socket to connect before issuing your read or write commands.
// If you do so before the socket is connected, it will simply queue the requests,
// and process them after the socket is connected.
// Also, you can issue multiple write commands (or read commands) at a time.
// You don't have to wait for one write operation to complete before sending another write command.
//
// The whole point is to make YOUR code easier to write, easier to read, and easier to maintain.
// Do networking stuff when it is easiest for you, or when it makes the most sense for you.
// AsyncSocket adapts to your schedule, not the other way around.
#if READ_HEADER_LINE_BY_LINE
// Now we tell the socket to read the first line of the http response header.
// As per the http protocol, we know each header line is terminated with a CRLF (carriage return, line feed).
[asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1.0 tag:0];
#else
// Now we tell the socket to read the full header for the http response.
// As per the http protocol, we know the header is terminated with two CRLF's (carriage return, line feed).
[asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1.0 tag:0];
#endif
}
- (void)socketDidSecure:(GCDAsyncSocket *)sock
{
// This method will be called if USE_SECURE_CONNECTION is set
DDLogVerbose(#"socketDidSecure:");
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
DDLogVerbose(#"socket:didWriteDataWithTag:");
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
DDLogVerbose(#"socket:didReadData:withTag:");
NSString *httpResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",httpResponse);
#if READ_HEADER_LINE_BY_LINE
DDLogInfo(#"Line httpResponse: %#", httpResponse);
// As per the http protocol, we know the header is terminated with two CRLF's.
// In other words, an empty line.
if ([data length] == 2) // 2 bytes = CRLF
{
DDLogInfo(#"<done>");
}
else
{
// Read the next line of the header
[asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1.0 tag:0];
}
#else
DDLogInfo(#"Full httpResponse: %#", httpResponse);
#endif
[httpResponse release];
}
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
// Since we requested HTTP/1.0, we expect the server to close the connection as soon as it has sent the response.
DDLogVerbose(#"socketDidDisconnect:withError: \"%#\"", err);
}
I've looked around for answers but have had no success. I figured the best course of action would be to ask you all rather than wrack my brain trying to solve it myself.
The solution was to connect to the local network differently. I was operating with a network that required a log in for local access but offered a "visitors" connection for wide area network access. The device (iPad) was automatically connecting as a "visitor" and I needed to log in manually.
So, if you aren't able to connect w/ this API, check out how your connecting to the network!