Search code examples
iosobjective-csocketsgcdasyncsocket

GCDAsyncSocket: [socket acceptOnPort: error:] not accepting


So I have been trying to create two GCDAsyncSocket's in my project, one (socket) that uploads a file to my server and the other (listenSocket) waits for another process from the server to communicate with it. In my ViewController I have initialized them in viewDidLoad method and setup delegate to self for both sockets.

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

I then made the listenSocket start listening by

NSError *err = nil;
if (![listenSocket acceptOnPort:19920 error:&err]) {
    NSLog(@"listenSocket failed to accept: %@", err);
}

I then made socket to connect to remote server and start uploading files.

The problem is that socket works fine and can upload and also read response from my server, but it seems that I can't access the accepting listenSocket by any way. Not by the other process on server, nor by using telnet or by typing the ip address and port number into browser. Why is this and how do I fix it?


Edit:

Here's what I am doing with my app:

I am working on an app that programs for Arduino on iPhone. Due to App Store policy the compiling and uploading process has to be on server, so I'm using socket to upload the code to server to get it compiled. In order to upload the compiled binaries to Arduino, I have to run avrdude which fortunately would accept an ip + port address instead of a usb connection as target. avrdude is implemented so that it connects to the address as a client, so I have to open a listening socket on my app.


Solution

  • I imagine your issue relates to your device not having a routable IP address from the outside world. I have assumed here that you are not testing on a local network with your server and phone both using this network via wifi/cable.

    When you are on your device using a mobile network, it is assigned an IP address from the mobile operator. This address is more than likely an address which is part of their internal mobile network. When you connect outside of the mobile network to a server, the address the server sees for your device is not the address you see on the device. The addresses are mapped in transit as your IP packet passes through the various gateways while on its way to the server. Thus when you server sees a connection requested on a listen socket, it has a reply address which when used allows the reply to traverse back to your device.

    A similar issue occurs when your device is on WiFi behind a NAT router. Connections made outgoing are seen by the router and it changes the from IP address to be that of the router. Since it sees the start of the conversation, it know where return packets with a given port and sequencing should be routed. However if somebody wants to connect to your device from outside, you have to have set up port forwarding on the router for a known port telling it where to send connection packets.

    So applying this to your situation:

    Outgoing Works (Why):

    Your outgoing socket works, because you are connecting to an externally visible IP. When you connect, the network knows exactly where the packet has to go and the reply address is provided in the packet by the network.

    Incoming Does Not Work (Why):

    Your listen socket will not be working because the address you are sending to does not exist on the open internet. To make a connection from the server or anywhere else to your device you would need an IP which has a routing mapped through to your device. If the device is on a mobile network, you need an external IP for the mobile network which maps to your device. If the device was behind a NAT router, you would need port forwarding set up.

    Solution:

    Unfortunately, there is no easy solution as your need an IP address for your device in the outside world. Much depends on your use case which you have not mentioned. You either need an external IP which is reliable or you need to use an intermediate server to handle messaging or you need to change your approach and have the device poll every so often for information.

    This is a problem which has existed for a long time and is why peer to peer companies have smart algoithms for connecting peer to peer services which use clever techniques like hole punching to connect devices.

    IMHO I would move to a model where your device always initiates the connection if you can.