簡體   English   中英

從InputStream讀取時SocketException錯誤的文件描述符

[英]SocketException bad file descriptor when reading from InputStream

我正在嘗試構建一個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();
    }

}

根據 Socket#getOutputStream的文檔

getOutputStream

OutputStream getOutputStream()

返回此套接字的輸出流。

如果此套接字具有關聯的通道,則結果輸出流會將其所有操作委托給該通道。 如果通道處於非阻塞模式,則輸出流的寫操作將引發IllegalBlockingModeException。

關閉返回的OutputStream將關閉關聯的套接字。

這里需要注意的是:

關閉返回的OutputStream將關閉關聯的套接字。

在您的代碼中,寫入輸出流之后:

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的公共接口來通知調用活動套接字超時。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM