Android - Socket Connected即使它不能

时间:2015-02-23 14:39:48

标签: android sockets

首先,我知道你的想法是什么,这是一个奇怪的问题,似乎不可能是真的,但在这里我就是这个......

该项目是一个项目,它通过套接字发送一个字节数组,并从套接字接收数据。我目前正致力于保护。我想确保用户知道套接字何时无法连接...您可以在下面查看连接代码。

所以我有一个Android应用程序连接到世界其他地方办公室的Windows Server计算机上的可执行文件。套接字通过通常的IP地址和端口号连接。

我正在使用Android 5.0(棒棒糖)手机测试应用程序......

现在有足够的无聊的东西: 如果我关闭服务器并完全关闭它并在wi-fi上测试它,那么套接字失败 - 这是正确的。它无法连接,它会抛出一个SocketException。这很好,并按预期工作。现在如果我关闭wi-fi并使用移动数据(我在阳光明媚的英国使用o2!)然后出现问题,使用相同的代码,服务器仍然关闭,我不抛出SocketException,相反,代码简单认为它已连接在套接字上。连接线程在下面。

Thread StartConnection = new Thread()
        {
            @Override
            public void run()
            {

                int dstPort = 10600;

                socket = new Socket();
                try
                {
                    ipaddress = InetAddress.getByName(dstName);
                }
                catch (UnknownHostException uhe)
                {
                    uhe.printStackTrace();
                }

                j = new InetSocketAddress(IPString , dstPort);

                try
                {
                        socket.setKeepAlive(true);
                        socket.setReuseAddress(true);
                        socket.setTcpNoDelay(true);
                        socket.setSoTimeout(5000);
                        socket.connect(j, 5000);
                        connected = true;
                        outputstream = socket.getOutputStream();
                        DataThread();
                        SocketError = false;
                }
                catch (SocketException se)
                {
                    se.printStackTrace();
                }
                catch (IllegalArgumentException iae)
                {
                    iae.printStackTrace();
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        };
        StartConnection.start();
  

socket.connect(j, 5000); 

工作正常没有套接字错误。没有超时。

outputstream = socket.getOutputStream(); 

也有效。我得到的结果是" java.net.PlainSocketImpl$PlainSocketOutputStream@48e6f6e"这也没关系。

socket.getInputStream()返回" java.net.PlainSocketImpl$PlainSocketInputStream@1ea305a5"

2 个答案:

答案 0 :(得分:3)

这很有趣。我一直在测试,可以复制你描述的行为。

首先,这里最高的投票答案提供了关于Java Socket API的很好的讨论:

Java socket API: How to tell if a connection has been closed?

由此,我想知道是否尝试从套接字读取一个字节是否允许我们检查它是否真的开放

// Endpoint that does not exist
Socket socket = new Socket("1.2.3.4", 1234);
InputStream tmpIn = socket.getInputStream();
int result = tmpIn.read();
Log.i("SocketTest", "read() result: " + result);

在端点无效的情况下,我发现对read()的调用在大约10秒后返回-1。

如果IP和端口上有监听器,则对read()的调用似乎无限期地阻塞,直到发生某些事情,例如实际从服务器接收数据或丢失互联网连接等。

基于这种差异,我已将此测试包装到一个新类中:

public class VerifiedSocket extends Socket {

    public VerifiedSocket(String ip, int port, int millisecondTimeout) throws UnknownHostException, IOException{
        super(ip,port);

        ReadThread tryRead = new ReadThread();      
        tryRead.start();
        try {
            tryRead.join(millisecondTimeout);
        } catch (InterruptedException e) {
        }
        if(tryRead.getReadValue()==-1){
            // We read -1 so assume endpoint invalid
            throw new IOException("Endpoint is invalid");
        }       
    }

    private class ReadThread extends Thread{
        private int readValue = 512; // Impossible byte value

        @Override
        public void run(){              
            try {
                InputStream input = getInputStream();
                readValue = input.read();
            } catch (IOException e) {                   
            }               
        }

        public int getReadValue(){
            return readValue;
        }
    }       
}

然后按如下方式测试:

try {
    VerifiedSocket socket = new VerifiedSocket("1.2.3.4", 1234, 20000);
    Log.i("SocketTest", "Endpoint is valid");
    socket.close();
} catch (UnknownHostException e) {      
    Log.e("SocketTest", e.getLocalizedMessage());
} catch (IOException e) {       
    Log.e("SocketTest", e.getLocalizedMessage());
}

如果我在Wifi上使用无效端点,VerifiedSocket的构造函数会在超时时抛出异常。如果我在移动数据上使用无效端点,则会抛出IOException,并且“端点无效”#39;信息。不完美,但希望它可以帮助你。

答案 1 :(得分:-1)

如果您运行代码的环境不能按照您的需要运行(无论出于何种原因,Robert已经编写了一个代码),您可以通过引入新的抽象来解决不良行为。

你可以做很多事来解决它;这是一个解决方案 - 在使用的​​协议(java.net.Socket)之上实现一个包装器,它将扩展Socket类并覆盖方法connect()/ getInputStream()/ getOutputStream()/ whatever-method-you-need()代码在返回有效流之前首先尝试真正联系服务器的方式。

在包装器中,将几个字节(客户端密钥)发送到服务器,服务器使用应答(服务器密钥)进行响应。保密密钥,只有客户端和服务器才能知道密钥。如果服务器没有响应超时,则抛出异常。

private class MySocket extends Socket {
    Socket inner;
    MySocket(Socket inner) {
        this.inner = inner;
    }

    @Override
    InputStream getInputStream() {
        // do your ping here
        // if ping fails, thrown an exception
        return inner.getInputStream();
    }

    ... override additional methods as you need
}

您可以在所有位置使用此ping模式来检测连接丢失。

我知道你正在做底层应该做的事情......但是如果它没有做你期望的事情,你能做什么?