java.net.ServerSocket.accept () doesn't return

2019-04-26 12:08发布

问题:

I am trying to make a way to telnet to an unrooted Droid. I have the INTERNET permission active, I have my device connected on the same network as my Mac OS X box via WiFi, and I am able to ping the port I opened.

In initial experiments, I got it to work on a rooted test device, but I had the socket handlers run on the UI Thread rather than a separate thread. Now that I have the network modules on a separate thread, I can't get ServerSocket.accept () to return. It works on Google's version of android (vanilla), but not on Samsung's or Sony-Ericsson's.

When I telnet to it, my attempt would time out, and logcat wouldn't print out any exceptions or errors.

Here is a link to a google-code repo of my code: Google-code Repository

I am running ServerScoket.accept () on a separate thread, and run the stream processors on another thread as well. Comments on my design (i.e. I should use Handlers or AsyncTasks) are extremely welcome. Right now, in order to Toast the messages received via telnet, I use a Handler with the looper being acquired via a Context.

The following is what I get when I run netstat -n on the adb shell on the non-working devices:

~$ adb shell netstat -n
Proto Recv-Q Send-Q Local Address          Foreign Address        State
tcp        0      0 127.0.0.1:7777         0.0.0.0:*              LISTEN
tcp        0      0 127.0.0.1:7203         0.0.0.0:*              LISTEN
tcp        0      0 127.0.0.1:47609        127.0.0.1:7777         ESTABLISHED
tcp        0      0 127.0.0.1:7777         127.0.0.1:47609        ESTABLISHED
tcp        0      0 127.0.0.1:47610        127.0.0.1:7777         ESTABLISHED
tcp        0      0 127.0.0.1:7777         127.0.0.1:47610        ESTABLISHED

The difference is that in the working devices, they list an IP with my port open in the state, LISTEN.

UPDATE: Having the <uses-permission android:name="INTERNET"/> set in my android_manifest, I tried changing the port number to 689. It didn't work; I got a BindException, saying that I may be lacking the INTERNET permission. So, I changed it to 1989, and I went back to everything working until accept (). I assume this is because I ran it on a non-root phone, and I don't have access to ports 1024 and below.

UPDATE: I ran a really similar program on my Mac, and it worked fine when I tried telnetting to my Mac using the IP address assigned to me. It didn't work when I tried telnetting from another Mac but it didn't seem to connect; the connection would timeout. It did work over an ad-hoc network, though. I still have yet to try it using the droid, but I will update this asap.

UPDATE: I managed to get the app working on 3 separate Droids running Vanilla (Android released by google). It worked on a Nexus, an Apanda A60 (my first device; adb has ceased to detect it for some reason.), and a custom-made, unbranded tablet. Still, because I already offered a pretty big bounty, I plan on seeing this through to the end.

As stated earlier, my app works with Vanilla versions of android, but not with modified versions. The three phones that failed to run it were all mid-range models; 2 Samsung GT-i5503s, and a Sony-Ericcson E16i.

回答1:

It seems like you have a networking issue, rather than a code issue. I used your latest project and it is listening on the port, as expected.

I added this to TelnetServer.setupServerSocket() to confirm some information:

Log.i("TelnetServer", "ServerSocket Address: " + this.server.getLocalSocketAddress());
try {
    Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
    while (en.hasMoreElements()) {
        NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                Log.i("TelnetServer", "Listen On: " + inetAddress.getHostAddress());
            }
        }
} catch (SocketException ex) {
    Log.e("TelnetServer", ex.toString(), ex);
}

This will print all of the addresses your service is listening on (if it is listening on 0.0.0.0/0.0.0.0:xxx (printed after ServerSocket Address:)).

You should run emulator with the -tcpdump <file> option and also provide this. It will confirm any connection is being attempted. My hunch is that your client is not able to access the server, which is why the server is not receiving the connection - rather than an issue with the code.

Please provide the tcpdump file, your IP Address (of the client) and the logcat output (including the ServerSocket Address and Listen On statements) for further analysis.



回答2:

See if netstat -n from the adb shell shows anything actually listening on the port you chose at the time when accept() is not returning.

Also realize that when not running as root, you can only bind unprivileged ports, of which the default telnet port is not an example. Does your code check that bind() was successful?

UPDATE:

Since the code works on a number of devices (where netstat -n would presumably list the socket) the failure to list it on the subject device should probably remain a focus. The Java ServerSocket methods depend on a socket factory which can be over-ridden to let you do distinct calls to socket(), bind(), and listen() specifying fuller details, so it may make sense to try your code that way. There's another case floating around where a device's attempt to support ipv6 seems to be causing someone similar problems, and at least on other java platforms creating the socket at a lower level to specify ipv4 seems like a promising answer.



回答3:

I understand you have tried most of the things are you are just few steps from getting it right on two particular devices.

Just a thought, If not point to point, why not Use Muti-casting for service registration and discovery in local area networks.

This is the java implementation JmDNS

and this is its Android demo

EDIT : Rather I should say to check connectivity with that two devices.