我正在尝试构建一个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();
}
}
答案 0 :(得分:2)
Socket#getOutputStream
类的According to the documentation:
<强>的getOutputStream 强>
OutputStream getOutputStream()
返回此套接字的输出流。
如果此套接字具有关联的通道,则生成输出 stream将其所有操作委托给该频道。如果是频道 在非阻塞模式下,输出流的写操作将是 抛出IllegalBlockingModeException。
关闭返回的OutputStream将关闭相关的套接字。
这里需要注意的是:
关闭返回的OutputStream将关闭相关的套接字。
在您的代码中,在您写入输出流之后:
out.write(buffer, 0, buffer.length);
你关闭了输出流
out.flush();
out.close();
根据文档,关闭套接字,因此关闭InputStream
。因此是例外。
在执行两个操作后,只需关闭套接字连接,而不是单独关闭每个流。
另请注意,flush()
之前close()
毫无用处。
答案 1 :(得分:0)
我使用了其他答案中的建议以及针对此问题发布的评论来解决此问题。然而,一个主要问题仍然存在。如果一次尝试发送消息失败,则服务器将永远不会完成并发送响应。我用来解决这个问题的方法是在连接的套接字上设置超时。我将读取循环替换为:
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的公共接口来通知调用活动套接字超时。