【问题标题】:SocketException bad file descriptor when reading from InputStream从 InputStream 读取时 SocketException 错误的文件描述符
【发布时间】:2018-02-11 15:02:13
【问题描述】:

我正在尝试构建一个 android 应用程序,用户可以在其中创建一个新帐户并让该帐户创建由服务器执行,并将信息添加到数据库中。

在服务器完成(或未能完成)这个任务后,我想要一个响应发送回客户端,指示任务成功或失败;我正在从我的异步任务类成员的同一个套接字打开一个inputstream 和一个outputstream

似乎代码在从客户端发送到服务器时执行良好,并且服务器正确接收了所有内容。但是当客户端收到服务器返回的响应时,我遇到了一个错误:

09-02 15:58:02.266 30979-32154/com.example.zemcd.messagebottle E/NewAccountTask: error connecting
    java.net.SocketException: recvfrom failed: EBADF (Bad file descriptor)
        at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:588)
        at libcore.io.IoBridge.recvfrom(IoBridge.java:552)
        at java.net.PlainSocketImpl.read(PlainSocketImpl.java:481)
        at java.net.PlainSocketImpl.-wrap0(PlainSocketImpl.java)
        at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
        at libcore.io.Streams.readSingleByte(Streams.java:41)
        at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:233)
        at com.example.zemcd.messagebottle.NewAccountTask.doInBackground(NewAccountTask.java:50)
        at com.example.zemcd.messagebottle.NewAccountTask.doInBackground(NewAccountTask.java:17)
        at android.os.AsyncTask$2.call(AsyncTask.java:295)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
        at java.lang.Thread.run(Thread.java:818)
    Caused by: android.system.ErrnoException: recvfrom failed: EBADF (Bad file descriptor)
        at libcore.io.Posix.recvfromBytes(Native Method)
        at libcore.io.Posix.recvfrom(Posix.java:189)
        at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:250)
        at libcore.io.IoBridge.recvfrom(IoBridge.java:549)

这是我的 android 客户端 asynctask 的代码,我已经指出了错误发生的位置:

public class NewAccountTask extends AsyncTask<Void, Void, Void> {
private static final String TAG = "NewAccountTask";
private static final String AUTH_KEY = "9LEF1D97001X!@:";
private static final String REQUEST_NEW_ACCOUNT = "0";
private static final int PORT = 5555;

private String mContactId;
private String mPassword;
private Context mContext;


private Socket mSocket;
private String response;

public NewAccountTask(String contactId, String password, Context context){
    mContactId = contactId + ":";
    mPassword = password + ":";
    mContext = context;
}

@Override
protected Void doInBackground(Void... params) {
    try{
        mSocket = new Socket("192.168.0.111", PORT);
        OutputStream out = mSocket.getOutputStream();
        InputStream in  = mSocket.getInputStream();
        //write key to server
        byte[] buffer = (AUTH_KEY + mContactId + mPassword + REQUEST_NEW_ACCOUNT).getBytes();
        out.write(buffer, 0, buffer.length);
        out.flush();
        out.close();

        //i posted a log message here this one runs
        int c;
        while((c = in.read()) != -1) { <--ERROR OCCURS HERE!
            response += (char) c;
        }
        //and here this one never runs

        in.close();

        Log.i(TAG, "connection success!");
    }catch (IOException ioe){
        Log.e(TAG, "error connecting", ioe);
    }

    return null;
}

@Override
public void onPostExecute(Void result){
    Toast.makeText(mContext, response, Toast.LENGTH_LONG).show();
}
}

这是服务器用来发送响应的方法:

    public void sendResponse(byte[] response) {
    OutputStream out;
    try {
        out = sock.getOutputStream();
        out.write(response, 0, response.length);
        out.flush();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

【问题讨论】:

  • 如果读取循环永远不会退出它只能是因为对等方没有关闭连接。有没有抛出异常?
  • @EJP 没有。它不会抛出异常。它永远不会退出。
  • 叹息。 peer 是否抛出异常,而不是关闭连接?
  • @EJP 没有。对等方不抛出异常。它只是停止。服务器开始接收,但似乎从未读取或写入任何数据。
  • 所以 server 有一个类似的读取循环,直到 client 关闭连接才会终止。这不是一个可行的方法。例如,您需要发送和阅读

标签: java android sockets inputstream


【解决方案1】:

According to the documentationSocket#getOutputStream 类:

getOutputStream

输出流 getOutputStream()

返回此套接字的输出流。

如果此套接字有关联的通道,则结果输出 流将其所有操作委托给通道。如果频道 处于非阻塞模式,则输出流的写操作将 抛出 IllegalBlockingModeException。

关闭返回的 OutputStream 将关闭关联的套接字。

这里需要注意的是:

关闭返回的 OutputStream 将关闭关联的套接字。

在你的代码中,在你写入输出流之后:

out.write(buffer, 0, buffer.length);

你关闭了输出流

out.flush();
out.close();

根据文档,关闭套接字并因此关闭关联的InputStream。因此例外。

这两个操作都执行完之后就关闭socket连接,而不是单独关闭每个流。

还要注意flush()close() 之前毫无用处。

【讨论】:

  • 请注意,这是 Android 中的一个错误。它应该抛出SocketException: socket closed
  • @EJP :根据我有限的知识,看起来异常是正确的。根据文档,close() 也将关闭套接字,因此将关闭关联的 InputStreamread() 操作(实际上引发异常)在关闭的 InputStream 上(因此与套接字并不直接相关) 并且问题中的例外对于封闭的InputStream 是可以的。我错过了什么吗?你能纠正我吗?
  • @ZeekHuge 我已经按照你的建议做了,但我遇到了一个新问题。当我在执行这两个操作后尝试关闭两个流时,应用程序在读取操作中被卡住了。我正在对我的代码进行编辑,指出它卡在哪里。
  • 抛出异常是对的,抛出this异常是不对的。它应该做我所说的,比如 Oracle JRE。它与套接字直接相关,通过文件描述符。
【解决方案2】:

我使用其他答案中的建议以及针对此问题发布的 cmets 来解决此问题。然而,一个主要问题仍然存在。如果一对一尝试发送消息失败,则服务器将永远不会完成并发送响应。我用来解决这个问题的方法是在正在连接的套接字上设置超时。我将读取循环替换为:

mSocket = new Socket();
mSocket.setSoTimeout(500);
SocketAddress socketAddress = new InetSocketAddress("192.168.0.111", PORT);
mSocket.connect(socketAddress);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
Log.d(TAG, "streams aquired");

out.write(AUTH_KEY + mContactId + mPassword + REQUEST_NEW_ACCOUNT);
Log.d(TAG, "data sent");
out.newLine();
out.flush();

Log.d(TAG, "read began");
response = in.readLine();
out.close();
in.close();
Log.d(TAG, "streams closed");

Log.i(TAG, "connection success!");

并使用 OnFailureListener 的公共接口来通知调用活动的套接字超时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-06
    • 2020-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-13
    • 2011-09-08
    相关资源
    最近更新 更多