GCDAsyncSocket on browser connects and disconnect immediately with "Socket closed by remote peer" error

806 views
Skip to first unread message

saini.v...@gmail.com

unread,
Nov 13, 2014, 5:41:29 PM11/13/14
to cocoaasy...@googlegroups.com
Hi

I am really new to Network programming and using GCDAsyncSocket for the first time. I am trying to implement an iOS app where 1 device acts a host and other devices(browsers) connect to it. I am using NSNetService/NSNetServiceBrowser for publishing/browsing part.

Now, my client/browser resolves the host's address properly and try to connect to it using following code:

func netServiceDidResolveAddress(sender: NSNetService) {

       // Connect with Service

       if (self.connectWithService(sender, hostname: sender.hostName!)) {

           println("Connecting with service: domainName= \(sender.domain), type= \(sender.type), name= \(sender.name), onPort= \(sender.port) and hostname: \(sender.hostName!)");

       } else {

           println("Unable to connect with service: domainName= \(sender.domain), type= \(sender.type), name= \(sender.name), onPort= \(sender.port)");

       }

}


private func connectWithService(service: NSNetService, hostname: String) ->Bool {


        var isConnecting:Bool = false;


       


        if (self.browserSocket == nil || !(self.browserSocket?.isConnected)!) {


            // Initialize Socket


            self.browserSocket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue());


           


            // Connect


            while (!isConnecting){


                var error: NSError?;


                println("Connecting port : \(service.port) and hostname: \(hostname)");


                if (self.browserSocket.connectToHost(hostname, onPort: UInt16(service.port), error: &error)) {


                    isConnecting = true;


                } else if (error != nil) {


                    println("Unable to connect to address. Error \(error) with user info \(error?.userInfo)");


                }


            }


        } else {


            println("Connecting is: \(self.browserSocket.isConnected)");


            isConnecting = self.browserSocket.isConnected;


        }


        return isConnecting;


    }

    // GCDAsyncSocketDelegate methods

   

   func socket(sock: GCDAsyncSocket!, didConnectToHost host: String!, port: UInt16) {

       println("Socket: \(sock) Did connect to host: \(host) on port:\(port)");

       

       // Start Reading

       sock.readDataToLength(UInt(sizeof(UInt64)), withTimeout: -1.0, tag: 0);

   }

   

   func socketDidDisconnect(sock: GCDAsyncSocket!, withError err: NSError!) {

       println("Socket \(sock) did disconnect with error \(err?)");


    }




Following is the Publisher code:


   override func viewDidLoad() {


        super.viewDidLoad()


        let barButton = UIBarButtonItem(title: "Stop Publishing", style: .Done, target: self, action: "stopPublishing");


        self.navigationItem.rightBarButtonItem = barButton;


        


        // Publish the service


        self.publishService();


    }


    // MARK: Button callbacks


    private func publishService() {


        // Initialize GCDAsyncSocket


        self.socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue());


        


        // Start listening for incoming connections


        var error: NSError?;


        if (self.socket.acceptOnPort(0, error: &error)) {


            // Initialize Service


            self.service = NSNetService(domain: "local.", type: "_mpstest._tcp", name: UIDevice.currentDevice().name, port:0);


            


            // Configure Service


            self.service.delegate = self;


            self.service.includesPeerToPeer = true;


            self.service.publishWithOptions(.ListenForConnections);


        } else {


            println("Unable to create socket. Error \(error?.description) with user info \(error?.userInfo)");


        }


    }




Now, the client connects with the host and hits following method:

    func socket(sock: GCDAsyncSocket!, didConnectToHost host: String!, port: UInt16) {



But then it immediately disconnects hitting following method with ErrorCode =7 "Socket closed by remote peer"

    func socketDidDisconnect(sock: GCDAsyncSocket!, withError err: NSError!) {


Following is the console log from Browser device:

Service found is : <NSNetService 0x15e2f710> local. _mpstest._tcp. iPad 30

Connecting port : 50797 and hostname: iPad-30.local.

Connecting with service: domainName= local., type= _mpstest._tcp., name= iPad 30, onPort= 50797 and hostname: iPad-30.local.

Socket <GCDAsyncSocket: 0x15d80020> did disconnect with error Optional(Error Domain=kCFStreamErrorDomainNetDB Code=8 "nodename nor servname provided, or not known" UserInfo=0x15e3c7a0 {NSLocalizedDescription=nodename nor servname provided, or not known})

Connecting port : 50797 and hostname: iPad-30.local.

Connecting with service: domainName= local., type= _mpstest._tcp., name= iPad 30, onPort= 50797 and hostname: iPad-30.local.

Socket: <GCDAsyncSocket: 0x15e59c20> Did connect to host: 169.254.78.98 on port:50797

Socket <GCDAsyncSocket: 0x15e59c20> did disconnect with error Optional(Error Domain=GCDAsyncSocketErrorDomain Code=7 "Socket closed by remote peer" UserInfo=0x15e5b070 {NSLocalizedDescription=Socket closed by remote peer})



Host socket delegate methods are never hit.

I am really stuck here. Any help as to what am I doing wrong here would be great.

Thanks
Vik

Message has been deleted

AG

unread,
Nov 14, 2014, 4:32:35 AM11/14/14
to cocoaasy...@googlegroups.com
It looks like the port you are advertising is not the port the socket is listening on.

In this line

if (self.socket.acceptOnPort(0, error: &error)) {

you let GCDAsyncSocket choose an arbitrary port to listen on (actually the OS chooses one for you …). Now, when you publish the service, you tell the NSNetService to choose an arbitrary port on its own:

self.service = NSNetService(domain: "local.", type: "_mpstest._tcp", name: UIDevice.currentDevice().name, port:0);

So you advertise a different port than the one you are listening on.

You can solve this in two ways. Let the socket choose a port and use this port when creating the NSNetService (port:self.socket.localPort). Or do it the other way around: let NSNetService choose a port and use this port when starting to accept incoming connections. I would prefer the first method.

Arno
...

saini.v...@gmail.com

unread,
Nov 17, 2014, 3:14:53 PM11/17/14
to cocoaasy...@googlegroups.com
Thanks for the reply AG. That makes perfect sense. Your suggestion made it work if I use following method to publish the service:

  self.service.publish();


However, if I publish it like this:
 

  self.service.publishWithOptions(.ListenForConnections);


then it does not work and I get the following error:

Socket <GCDAsyncSocket: 0x17e946b0> did disconnect with error Optional(Error Domain=NSPOSIXErrorDomain Code=49 "Can't assign requested address" UserInfo=0x17e96df0 {NSLocalizedFailureReason=Error in connect() function, NSLocalizedDescription=Can't assign requested address})


Do you have insight as to what is happening here and what am I doing wrong in this context?

I really appreciate your help.

Thanks
Vik

Arno Gramatke

unread,
Nov 17, 2014, 4:58:05 PM11/17/14
to cocoaasy...@googlegroups.com
The NSNetService documentation states for ListenForConnections: "Specifies that a TCP listener should be started for both IPv4 and IPv6 on the port specified by this service." This is obviously not what you want, because you use GCDAsyncSocket for this.

You should get your head around, what you are trying to achieve and which of the different pieces should do what. As far as I can tell you use NSNetService to announce your service (and that's it, no connection handling, just announcing). GCDAsyncSocket then handles the connection itself.
--
You received this message because you are subscribed to the Google Groups "CocoaAsyncSocket" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cocoaasyncsock...@googlegroups.com.
To post to this group, send email to cocoaasy...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cocoaasyncsocket/87c8e2bb-99b0-4536-9de4-591485bb5d99%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

saini.v...@gmail.com

unread,
Nov 17, 2014, 6:20:44 PM11/17/14
to cocoaasy...@googlegroups.com
Thanks AG. I think I got it. I believe ListenForConnections should be used if we are trying to establish connection with something like NSStreams, etc and not in case of GCDAsyncSockets. I got confused earlier looking at the tutorial I was following. Thanks once again

-Vik
Reply all
Reply to author
Forward
0 new messages