koush / AndroidAsync

Asynchronous socket, http(s) (client+server) and websocket library for android. Based on nio, not threads.
Other
7.52k stars 1.56k forks source link

Get TimeoutException if in certain network #117

Open pakaufmann opened 10 years ago

pakaufmann commented 10 years ago

We have a serious problem with Ion and AndroidAsync.

If our client connects the app in a certain network, no call with ION will ever connect successfully. All we get is a TimeoutException while trying to connect to the URL.

Output with LogLevel Verbose:

03-10 11:10:23.444 14945-14985/? D/GOOGLEREQUEST﹕ (0 ms) http://www.google.ch: Executing request. 03-10 11:10:23.454 14945-14985/? D/GOOGLEREQUEST﹕ (18 ms) http://www.google.ch: Connecting socket 03-10 11:10:26.457 14945-14985/? E/GOOGLEREQUEST﹕ (3010 ms) http://www.google.ch: Connection error 03-10 11:10:26.467 14945-14985/? E/GOOGLEREQUEST﹕ null java.util.concurrent.TimeoutException at com.koushikdutta.async.http.AsyncHttpClient$2.run(AsyncHttpClient.java:184) at com.koushikdutta.async.AsyncServer.lockAndRunQueue(AsyncServer.java:665) at com.koushikdutta.async.AsyncServer.runLoop(AsyncServer.java:676) at com.koushikdutta.async.AsyncServer.run(AsyncServer.java:583) at com.koushikdutta.async.AsyncServer.access$700(AsyncServer.java:36) at com.koushikdutta.async.AsyncServer$13.run(AsyncServer.java:531)

The interesting thing is, if we make the same call to the same URL with a simple DefaultHttpClient of the android core API it works every time.

Is there a workaround for this, as I'm not really keen on changing all my API calls to work with the DefaultHttpClient instead of ION.

pakaufmann commented 10 years ago

After further investigation I found the actual source of the problem. Apparently the SocketChannel can never make a successful connection in the class AsyncServer. It just waits and waits until the timeout is reached.

Is there anything I can do about that? My guess is, that something from the network is blocking this SocketChannel from opening, but why is the DefaultHttpClient working then?

pakaufmann commented 10 years ago

Just for other people with the same problem. We were behind a proxy, which obviously blocked our attempts to create a Socket directly. What we did was the following. We created a method "setProxy", which detects the proxy setup on Android and uses this to set the proxy for the Ion request.

private void setProxy(Builders.Any.B builder, String url)
{
    List<Proxy> proxies = null;
    try
    {
        proxies = ProxySelector.getDefault().select(new URI(url));
    }
    catch(URISyntaxException e)
    {
        e.printStackTrace();
    }

    if(proxies != null)
    {
        for(Proxy proxy : proxies)
        {
            InetSocketAddress address = (InetSocketAddress)proxy.address();

            if(address != null)
            {
                builder.proxy(address.getHostName(), address.getPort());
            }
        }
    }
}
koush commented 10 years ago

Good find. I should build this into the library itself I think.

pakaufmann commented 10 years ago

Yes. If I ever find the time I planned to make a pull request, but at the moment the solution works.

swatigoel commented 10 years ago

@kufi I am also facing the problem of Timeout. But in my case, it happens after socket is connected.

These are the socket connection logs:

06-23 15:36:20.039: I/Socket.IO(7440): (0 ms) http://url:port/socket.io/1/: Reconnecting socket.io 06-23 15:36:20.101: D/Socket.IO(7440): (0 ms) http://url:port/socket.io/1/: Executing request. 06-23 15:36:20.101: D/Socket.IO(7440): (0 ms) http://url:port/socket.io/1/: Connecting socket 06-23 15:36:20.304: V/Socket.IO(7440): (208 ms) http://url:port/socket.io/1/: socket connected 06-23 15:36:20.320: V/Socket.IO(7440): (217 ms) http://url:port/socket.io/1/: 06-23 15:36:20.320: V/Socket.IO(7440): POST /socket.io/1/ HTTP/1.1 06-23 15:36:20.320: V/Socket.IO(7440): Host: url:port 06-23 15:36:20.320: V/Socket.IO(7440): User-Agent: Dalvik/1.4.0 (Linux; U; Android 2.3.6; MB865 Build/5.5.1-175EDMR1.25) 06-23 15:36:20.320: V/Socket.IO(7440): Accept-Encoding: gzip, deflate 06-23 15:36:20.320: V/Socket.IO(7440): Connection: keep-alive 06-23 15:36:20.320: V/Socket.IO(7440): Accept: /_ 06-23 15:36:20.320: V/Socket.IO(7440): 06-23 15:36:20.320: V/Socket.IO(7440): (219 ms) http://url:port/socket.io/1/: request completed 06-23 15:36:50.429: E/Socket.IO(7440): (30323 ms) http://url:port/socket.io/1/: Connection error 06-23 15:36:50.429: E/Socket.IO(7440): null 06-23 15:36:50.429: E/Socket.IO(7440): java.util.concurrent.TimeoutException 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.http.AsyncHttpClient$2.run(AsyncHttpClient.java:196) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.lockAndRunQueue(AsyncServer.java:675) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.runLoop(AsyncServer.java:692) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.run(AsyncServer.java:600) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.access$700(AsyncServer.java:37) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer$13.run(AsyncServer.java:549) 06-23 15:36:50.429: E/Socket.IO(7440): (30326 ms) http://url:port/socket.io/1/: socket.io disconnected 06-23 15:36:50.429: E/Socket.IO(7440): null 06-23 15:36:50.429: E/Socket.IO(7440): java.util.concurrent.TimeoutException 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.http.AsyncHttpClient$2.run(AsyncHttpClient.java:196) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.lockAndRunQueue(AsyncServer.java:675) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.runLoop(AsyncServer.java:692) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.run(AsyncServer.java:600) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer.access$700(AsyncServer.java:37) 06-23 15:36:50.429: E/Socket.IO(7440): at com.koushikdutta.async.AsyncServer$13.run(AsyncServer.java:549)

For security reason, I have not mentioned actual url and port number.

I have used below code to establish socket connection and sending data on socket.

                            SocketIORequest req = new SocketIORequest("http://"+url+":"+port);
                req.setLogging("Socket.IO", Log.VERBOSE);
                ConnectCallback callback = new ConnectCallback() {

                    @Override
                    public void onConnectCompleted(Exception ex, SocketIOClient client) {
                        if (ex != null) {
                            ex.printStackTrace();
                            return;
                        }
                        Log.v("socket", "ESTABLISHED");

                        client.setDisconnectCallback(new DisconnectCallback() {
                            @Override
                            public void onDisconnect(Exception e) {
                                Log.v("socket", "TERMINATED");
                            }
                        });

                        client.setErrorCallback(new ErrorCallback() {
                            @Override
                            public void onError(String error) {
                                Log.e("socket", error);
                            }
                        });

                        client.setExceptionCallback(new ExceptionCallback() {
                            @Override
                            public void onException(Exception e) {
                                e.printStackTrace();
                            }
                        });

                        client.setReconnectCallback(new ReconnectCallback() {
                            @Override
                            public void onReconnect() {
                                Log.v("socket", "RECONNECTED");
                            }
                        });

                        client.setStringCallback(new StringCallback() {
                            @Override
                            public void onString(String string,
                                    Acknowledge acknowledge) {
                                Log.v("socket", "onEvent: "+string);
                            }
                        });

                        client.on(subOrUnsub, new EventCallback() {
                            @Override
                            public void onEvent(JSONArray array, Acknowledge acknowledge) {
                                Log.v("socket", "onEvent: "+array.toString());
                            }
                        });

                        client.setJSONCallback(new JSONCallback() {
                            @Override
                            public void onJSON(JSONObject json,
                                    Acknowledge arg1) {
                                Log.v("socket", "onJSON: "+json.toString());
                            }
                        });
                    }
                };
                         socket =  SocketIOClient.connect(AsyncHttpClient.getDefaultInstance(), req, callback).get();

I sent that "subOrUnsub" request that I need to write on socket, but before getting response of that request, socket connection is timed out.

sojharo commented 10 years ago

Koush,

Have you included this fix in the library? I was using your old library Android_websockets. When I realized about this problem that client can't connect when it is behind proxy, I googled the issue and came to this discussion.

In my project, I only want to use socket.io implementation. I was not able to understand how to use that setProxy function. Also, can you direct me to any tutorial on use of this library? I don't know how to shift to this new library from the old one. Kindly, if you can give me some steps to get started with this library, it would be helpful.

pakaufmann commented 10 years ago

Actually we modified the method slightly in our programm, because of a problem with certain 3G networks. The current method looks more or less like this:

protected void setProxy(Builders.Any.B builder, String url)
{
    //check for enabled wifi state.
    //Fix for potential problem on 3G-networks (e.g. Swisscom), as they set a proxy for GPRS which is
    //found over the ProxyList and then set on the connection. This seems to lead to some problems with the server not responding
    //correctly.
    if(!isWifiConnected()) return;

    List<Proxy> proxies = null;
    try
    {
        proxies = ProxySelector.getDefault().select(new URI(url));
    }
    catch(URISyntaxException e)
    {
        e.printStackTrace();
    }

    if(proxies != null)
    {
        for(Proxy proxy : proxies)
        {
            InetSocketAddress address = (InetSocketAddress)proxy.address();

            if(address != null)
            {
                builder.proxy(address.getHostName(), address.getPort());
            }
        }
    }
}

As far as I know this method is only usable with Ion, but it should probably be possible to reprogramm it for AndroidAsync. For Ion, just pass in an Ion builder and it'll do the rest.

vineetv2821993 commented 9 years ago

Hi Everyone, Is this issue resolved and fixed in main library? i am trying to make web socket connection to ws://echo.websocket.org (http://www.websocket.org/echo.html)

W/System.err﹕ java.util.concurrent.TimeoutException W/System.err﹕ at com.koushikdutta.async.http.AsyncHttpClient$2.run(AsyncHttpClient.java:247) W/System.err﹕ at com.koushikdutta.async.AsyncServer.lockAndRunQueue(AsyncServer.java:709) W/System.err﹕ at com.koushikdutta.async.AsyncServer.runLoop(AsyncServer.java:726) W/System.err﹕ at com.koushikdutta.async.AsyncServer.run(AsyncServer.java:627) W/System.err﹕ at com.koushikdutta.async.AsyncServer.access$700(AsyncServer.java:41) W/System.err﹕ at com.koushikdutta.async.AsyncServer$13.run(AsyncServer.java:569)

I wrote this code in OnClick of a button :-1:

                AsyncHttpClient.getDefaultInstance().websocket("ws://echo.websocket.org", "ws", new AsyncHttpClient.WebSocketConnectCallback() {
                    @Override
                    public void onCompleted(Exception ex, WebSocket webSocket) {
                        if (ex != null) {
                            ex.printStackTrace();
                            Log.d("WebSocket", "Failed");
                            return;
                        }
                        webSocket.send("Socket Test");
                        webSocket.send(new byte[10]);
                        webSocket.setStringCallback(new WebSocket.StringCallback() {
                            public void onStringAvailable(String s) {
                                Log.d("WebSocket", "I got a string: " + s);
                            }
                        });
                        webSocket.setDataCallback(new DataCallback() {
                            public void onDataAvailable(DataEmitter emitter, ByteBufferList byteBufferList) {
                                Log.d("WebSocket", "I got some bytes!");
                                // note that this data has been read
                                byteBufferList.recycle();
                            }
                        });
                    }
                });