从InputStream读取时,SocketException不良文件描述符



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

服务器完成(或未能完成)此任务后,我希望将响应发送回客户,以指示任务的成功或失败;我正在从我的异步任务类中的同一套接字中打开inputstreamoutputstream

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

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客户端异步的代码,我指出了错误发生的位置:

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

根据Socket#getOutputStream类的文档:

getOutputStream

outputStream getOutputstream()

返回此插座的输出流。

如果此插座具有关联的通道,则结果输出 流将其所有操作委派给频道。如果是频道 是在非阻滞模式下,然后输出流的写操作将 扔一个illegalblockingmodeexception。

关闭返回的输出流将关闭关联的套接字。

这里需要注意的是:

关闭返回的输出流将关闭关联的套接字。

在您的代码中,写入Ouput流之后:

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

您关闭了输出流

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

根据文档,关闭插座,因此关闭了相关的InputStream。因此例外。

执行两个操作后,就关闭套接字连接,而不是分开关闭。

还请注意,flush()close()之前是没有用的。

我在其他答案中使用了这些建议,以及为此问题发布的评论来解决此问题。但是,仍然存在一个主要问题。如果一个尝试发送消息的尝试失败,则服务器将永远不会完成并发送响应。我用来解决的方法是在连接的插座上设置超时。我用:

替换了读取循环
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通知套接字的计时活动。

最新更新