在Android上,java.net.ServerSocket.accept()方法不会返回

7
我正在尝试创建一种连接未root的Droid的telnet方式。我已经启用了INTERNET权限,我将我的设备通过WiFi连接到与我的Mac OS X框相同的网络上,并且我能够ping通我打开的端口。
在最初的实验中,我让套接字处理程序在UI Thread而不是单独的线程上运行,它可以在已root的测试设备上工作。现在我把网络模块放在了单独的线程上,但我无法使ServerSocket.accept()返回。它可以在Google的Android版本(vanilla)上工作,但不能在三星或索尼爱立信上工作。
当我尝试进行telnet时,我的尝试会超时,而logcat不会打印任何异常或错误。
这是我的代码的谷歌代码库链接: Google-code Repository 我正在单独的线程上运行ServerSocket.accept(),并在另一个线程上运行流处理器。对于我的设计意见(例如,我应该使用HandlerAsyncTasks),非常欢迎。目前,为了通过telnet接收到的消息展示Toast,我使用具有通过Context获取的looper的Handler。
当我在不工作的设备上通过adb shell运行netstat -n时,我得到以下内容:
~$ 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

区别在于,在工作设备中,他们列出了一个带有我的端口开放状态LISTEN的IP。
更新:在我的android清单中设置了后,我尝试将端口号更改为689。它没有起作用;我得到了一个BindException,说我可能缺少INTERNET权限。所以,我将其改为1989,并回到一切正常,直到accept()。我认为这是因为我在非root手机上运行它,我无法访问1024及以下端口。
更新:我在我的Mac上运行了一个非常类似的程序,当我使用分配给我的IP地址从telnet连接到我的Mac时,它可以正常工作。当我试图从另一个Mac上进行telnet连接时,它并没有连接成功;连接会超时。但是在adhoc网络上它确实可以工作。我仍然需要尝试使用droid,但我会尽快更新。 更新: 我成功让这个应用在三台使用原生安卓系统的Droid上运行。它能在Nexus,Apanda A60(我的第一台设备,由于某些原因adb已经停止检测),以及一个自制的非品牌平板电脑上运行。尽管如此,因为我已经提供了相当大的悬赏,我打算将其进行到底。

正如之前所述,我的应用适用于原生安卓系统,但不适用于修改过的版本。未能运行该应用程序的三部手机都是中档型号;两部三星GT-i5503和一部索尼爱立信E16i。


请记住,您只能“ping” IP地址,而不能“ping”端口。因此,您成功的ping仅告诉您该设备已连接到网络。您能否发布启动服务器套接字的代码? - Jack
不,你没有这样做,因为ping不需要端口。 - Chris Stratton
我现在要读取“man ping”。显然,“ping 192.168.1.3”会超时,而“ping 192.168.1.3:.*”有效(.*代表一个正则表达式)。 - cesar
1
@anonymous Android 运行在 *nix 上,'ping' 发送的是 UDP 回显 - 需要一个端口。如果它运行在 Windows 上(例如 Windows XP 开发机器),它将发送一个不需要端口的 ICMP 'ping'。 - CrackerJack9
@匿名的,你能否分享一下在调用accept()周围的代码以及你所更改的内容,以便它停止工作? - CrackerJack9
显示剩余7条评论
3个回答

1

看起来你的问题是网络问题,而不是代码问题。我使用了你最新的项目,它正在按预期监听端口。

我在 TelnetServer.setupServerSocket() 中添加了这个以确认一些信息:

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);
}

这将打印出您的服务正在侦听的所有地址(如果它正在侦听0.0.0.0/0.0.0.0:xxx(在ServerSocket地址之后打印))。

您应该使用emulator选项运行-tcpdump <file>,并提供此选项。它将确认是否尝试连接任何连接。我的直觉是您的客户端无法访问服务器,这就是为什么服务器没有接收到连接的原因,而不是代码问题。

请提供tcpdump文件、您的IP地址(客户端)和logcat输出(包括ServerSocket地址和Listen On语句)以进行进一步分析。


说实话,我还没有时间确认这是否是网络错误,但目前来看,这是我最好的假设。顺便说一句,你确实告诉过我ping的工作原理,所以也有那个。 - cesar

1

查看从adb shell运行的netstat -n命令是否显示在您选择的端口上实际正在侦听的内容,此时accept()未返回任何内容。

还要注意,当不以root身份运行时,您只能绑定非特权端口,其中默认的telnet端口不是一个例子。您的代码是否检查了bind()是否成功?

更新:

由于该代码在许多设备上运行正常(其中netstat -n可能会列出套接字),因此无法在主题设备上列出它应该保持关注。Java ServerSocket方法依赖于可以覆盖的套接字工厂,以让您执行对socket()、bind()和listen()的不同调用,指定更完整的详细信息,因此尝试使用这种方式运行代码可能是有意义的。还有另一种情况,即设备尝试支持ipv6似乎会导致类似问题,至少在其他java平台上,在较低级别创建套接字以指定ipv4似乎是一个有前途的答案。


抱歉,我刚刚才弄明白了adb shell,所以我运行了它并发现我的端口不在列表中。 - cesar
是的。我将它添加到“uses-permission”下的权限选项卡中。我也没有收到任何关于需要INTERNET权限的错误或异常(我记得在之前的尝试中遇到过这个问题)。 - cesar
我更改了 setupServerSocket() 的返回语句;我将其从 return true 改为返回 this.socket.isBound(),这将返回 true,但仍无法正常工作或在 netstat -n 中显示。 - cesar
你确定数据包是否已经到达机器了吗?当你说 accept() 没有返回时,为什么你认为它应该返回呢? - CrackerJack9
@Chris:我的 InetAddress() 被设置为 0.0.0.0,但我尝试将其设置为分配给我的 Droid 的 IP,但结果相同。我的端口通常设置为 19898989,但我曾尝试将其设置为 689。我得到了一个异常,这意味着如果我使用低于 1024 的端口,它确实会抛出异常,进一步暗示这不是问题。不过,我可能错了。 - cesar
显示剩余7条评论

0

我知道你已经尝试了大部分的方法,你离在两个特定设备上正确运行只有几步之遥。

不妨考虑一下,如果不是点对点通信,为什么不在本地网络中使用多播进行服务注册和发现呢?

这是Java实现JmDNS

这是它的Android演示

编辑:更确切地说,应该检查与这两个设备的连接性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接