Android客户端Socket发图后提前关闭无法接收服务端回执如何解决
解决方案
核心问题原因
- TCP是流式传输协议,没有天然的数据边界,你直接发送图片字节,服务端无法判断图片是否传输完成,会一直阻塞在
recv调用等待新数据,不会触发回执返回逻辑。 - Java Socket的输出流调用
close()会直接关闭整个Socket关联的所有IO通道,输入流也会同步失效,自然无法接收服务端返回的回执。 - 客户端当前代码中
while (isSocketOpen)是死循环,只要isSocketOpen没有被主动设为false,就会重复发送图片数据,导致服务端接收的文件体积持续变大。 - 服务端的回执发送逻辑错误写在了文件写入异常的
except块中,正常保存图片成功的场景根本不会发送回执;且recv返回空仅当客户端主动断开连接时才会触发,正常传输完成不会进入该分支。
修改后的Python服务端代码
from socket import * import uuid import struct port = 9999 s = socket(AF_INET, SOCK_STREAM) s.bind(('', port)) s.listen(1) while True: conn, addr = s.accept() img_dir = '/home/Desktop/frames_saved/' img_format = '.png' try: print("Connected by ", addr) filename = str(uuid.uuid4()) # 先读取4字节的图片长度(大端字节序) len_data = conn.recv(4) img_len = struct.unpack('!I', len_data)[0] # 接收对应长度的图片数据 received_data = b'' while len(received_data) < img_len: chunk = conn.recv(min(1024*8, img_len - len(received_data))) if not chunk: break received_data += chunk # 保存图片 with open(img_dir+filename+img_format, 'wb') as file: file.write(received_data) # 发送回执,末尾加换行符适配客户端readLine接口 conn.sendall("接收成功\n".encode('utf-8')) except Exception as e: print(f"处理出错: {e}") conn.sendall("接收失败\n".encode('utf-8')) finally: conn.close()
修改后的Android客户端代码
public class SendImageClient extends AsyncTask<byte[], Void, String> { @Override protected String doInBackground(byte[]... voids) { Socket socket = null; DataOutputStream dataOutputStream = null; BufferedReader input = null; String response = null; try { socket = new Socket("192.168.0.14",9999); dataOutputStream = new DataOutputStream(socket.getOutputStream()); input = new BufferedReader(new InputStreamReader(socket.getInputStream())); byte[] imgData = voids[0]; // 先发送4字节的图片长度(大端字节序,和服务端对应) dataOutputStream.writeInt(imgData.length); // 发送图片内容 dataOutputStream.write(imgData, 0, imgData.length); dataOutputStream.flush(); // 等待接收服务端回执 response = input.readLine(); Log.d("[回执内容]", response); } catch (IOException e) { e.printStackTrace(); } finally { // 统一回收资源,避免提前关闭Socket try { if (input != null) input.close(); if (dataOutputStream != null) dataOutputStream.close(); if (socket != null) socket.close(); } catch (IOException e) { e.printStackTrace(); } } return response; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); // 主线程弹出Toast if (result != null) { Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "传输失败", Toast.LENGTH_SHORT).show(); } } }
注意事项
- 如果需要多次传输图片,不要每次传输都新建Socket,可以复用同一个Socket连接,每次都遵循「发长度→发内容→等回执」的逻辑即可。
- 实际生产环境建议加上超时逻辑,避免网络异常时线程长时间阻塞。
内容的提问来源于stack exchange,提问作者user1159788
相关产品推荐
相关产品推荐

