无法在Jelly Bean上读取套接字InputStream

时间:2013-03-01 09:10:30

标签: java android sockets inputstream android-4.2-jelly-bean

我有一个TCP套接字连接,在Android 2.3上运行良好但现在面临Android 4.1上的一些问题。 问题是InputStream.read()方法总是返回-1(没有阻塞),就像连接关闭一样。

创建套接字:

SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket("c.whatsapp.net", 5222);
socket.setSoTimeout(3*60*1000);
socket.setTcpNoDelay(true);

检索输入和输出流并编写一些初始数据:

InputStream inputStream = new BufferedInputStream(socket.getInputStream());
OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());

outputStream.write(87);
outputStream.write(65);
outputStream.write(1);
outputStream.write(2);
outputStream.flush();

然后,这个条件总是在没有阻塞的情况下通过:

int c = inputStream.read();
if (c < 0) {
    Log.d(TAG, "End of stream");
}

此代码在后台线程中运行。它正在开发姜饼。

尝试使用InputStreamReader和OutputStreamWriter而不是直接流 - 没有效果。

7 个答案:

答案 0 :(得分:1)

之前我看过同样的错误,虽然这个答案可能看起来像offtopic给它一个机会,让我知道它是否有效,由于某种原因,套接字在jellybean上有奇怪的行为,即使它们在下层机器人工作完全正常版本,我解决这个问题的方法是将targetSdkVersion移动到果冻豆以及项目的Android属性下的Project Build Target,没有修改一行代码,只是因为某些原因,它确实特技...

希望这有帮助。

问候!

答案 1 :(得分:0)

我有一些类似的问题,inputStream.read()返回-1,我没有得到任何异常。事实上,服务器已关闭,连接断开。我没有用不同的版本测试它,只用4.0。

这是关于此行为的Google Bug Report

不幸的是,这个bug的状态似乎是“关闭”的,因为它不可重现。

我的工作是将-1解释为套接字的关闭和无法访问的服务器。当您尝试重新连接时,您会收到正确的错误。

答案 2 :(得分:0)

朋友,

尝试inputStream.readLine();(即)DataInputStream.readLine();(弃用方法)

这对我有用......

答案 3 :(得分:0)

我遇到了类似的问题,并通过像这样的解决方法修复了它

private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

private static class WatchDog implements Runnable{
    private Thread thread = Thread.currentThread();

    public void run() {
        Log.d(LOG_TAG, "Interrupting read due to timeout");
        thread.interrupt();
    }
}

private void read(InputStream in, ByteBuffer bb, long waitTime) throws IOException {
    int startingPos = bb.position();
    long timeout = System.currentTimeMillis() + RESPONSE_TIMEOUT;


    ScheduledFuture<?> watchdogFuture = executor.schedule(new WatchDog(), RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS);
    try {
        while(System.currentTimeMillis() < timeout && bb.hasRemaining()){ //workaround fixing timeout after 1ms
            try{
                int read = in.read(bb.array(), bb.position(), bb.remaining());
                if(read > 0){
                    bb.position(bb.position()+read);
                }
            } catch(SocketTimeoutException e){}
            if(bb.hasRemaining()){
                Thread.sleep(5);
            }
        }
        watchdogFuture.cancel(true);
    } catch (InterruptedException e) {}


    if(bb.hasRemaining()){
        throw new SocketTimeoutException("Unable to read requested bytes: " 
                + (bb.position()-startingPos) + "/" +  (bb.limit()-startingPos)
                + " after " + (System.currentTimeMillis() - timeout + RESPONSE_TIMEOUT) + "ms");
    }
}

答案 4 :(得分:0)

使用BufferedReader和PrintWriter可以为我的所有版本工作,非常方便通过任何通信协议发送和接收任何你想要的东西(甚至是JSON字符串)。尝试在启动后台线程时将它们保存为成员变量:

mInput = new BufferedReader(new InputStreamReader(
            socket.getInputStream()));
mOutput = new PrintWriter(new BufferedWriter(
            new OutputStreamWriter(socket.getOutputStream())), true);

对于异步通信,您的后台线程可能如下所示:

@Override
public final void run() {
    while (!Thread.currentThread().isInterrupted()) {
        if (mInput == null) {
            break;
        }
        String message = null;
        try {
            message = mInput.readLine();
        } catch (IOException e) {
            // handle the exception as you like
            break;
        }
        if (Thread.currentThread().isInterrupted()) {
            // thread was interrupted while reading
            break;
        } else if (message != null) {
            // handle the message as you like
        }
    }
}

使用其他后台线程发送消息:

@Override
public void run() {
    if (mOutput != null) {
        mOutput.println(<message to be );
        if (mOutput == null) {
            // the above thread was interrupted while writing
        } else if (!mOutput.checkError()) {
            // everything went fine
        } else {
            // handle the exception
        }
    }
}

此外,您必须关闭来自外部的流,以确保readLine不会永久阻止:

try {
    mOutput.close();
    mInput.close();
    mOutput = null;
    mInput = null;
} catch (IOException e) {
    // log the exception
}

现在,由于您正在使用TCP套接字,因此套接字实际上可能已经死亡且readLine仍然阻塞。你必须像上面那样检测到并关闭流。为此,你必须添加另一个周期性发送keep-alive-messages的线程(哦)。如果在X秒内未从远程设备收到任何消息,则必须关闭流。

这整个方法确保套接字关闭,所有线程在所有情况下都完成。当然,通过删除sender-thread并在reader-thread中包含println(),您可以使通信同步,如果这是您需要的。我希望能帮到你(即使答案迟到了8个月)。

答案 5 :(得分:0)

试试这段代码 -

Runnable runnable = new Runnable() {

    @Override
    public void run() {

        synchronized (this) {
            Socket s = null;
            String inMsg = null, msg2 = null;
            try {
                try {
                    s = new Socket(server, port);
                } catch (Exception e) {
                    return;
                }
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(s.getInputStream()));
                BufferedWriter out = new BufferedWriter(
                        new OutputStreamWriter(s.getOutputStream()));
                try {
                    inMsg = in.readLine()
                            + System.getProperty("line.separator");
                } catch (Exception e) {
                    return;
                }

                out.write(message + "\n\r");
                out.flush();
                try {
                    msg2 = in.readLine();
                    if (msg2 == null) {
                        return;
                    }
                } catch (Exception e) {
                    return;
                }
                out.close();
                s.close();
            } catch (Exception e) {
                return;
            }
        }

    }

};

它对我有用。

答案 6 :(得分:-1)