Android通过TCP Socket向Java PC服务器传图时数组越界问题求助
嘿,我来帮你排查这个问题!你遇到的数组索引越界错误,大概率是因为服务器端硬编码了文件大小,同时客户端没有正确传输完整的文件数据,甚至可能出现读取到的字节数为负数的情况,导致current变量出现异常值,最终触发write方法的参数错误。另外,客户端的文件读取逻辑也有漏洞,BufferedInputStream.read()不一定能一次性读完整个文件,这会导致服务器端接收的数据不完整或者陷入等待。
核心问题拆解
- 服务器硬编码文件大小:你把
filesize固定成了6022386,但实际传输的图片大小大概率和这个值不匹配。如果图片比这个大,数组会不够用;如果比这个小,一旦客户端传输出问题导致current变成负数(比如第一次read就返回-1),就会触发write方法的参数错误。 - 客户端未完整读取文件:
bis.read(mybytearray,0,mybytearray.length)方法不能保证一次性读取整个文件的所有字节,它只会读取当前流中可用的字节,剩余字节会留在文件流里没被发送,导致服务器接收的数据不完整。 - 服务器存在无效代码:代码里的
out.close();是错误的,没有定义out变量,这会直接导致编译失败。
修复后的客户端代码
客户端需要先向服务器发送文件大小,再循环发送文件内容,同时增加基础的错误校验:
package com.example.sclient; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; public class SendfileActivity extends Activity { private static final int SELECT_PICTURE = 1; private String selectedImagePath; private ImageView img; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); img = (ImageView) findViewById(R.id.ivPic); // 选择图片按钮逻辑 ((Button) findViewById(R.id.bBrowse)).setOnClickListener(v -> { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE); }); Button send = (Button) findViewById(R.id.bSend); final TextView status = (TextView) findViewById(R.id.tvStatus); // 发送图片按钮逻辑 send.setOnClickListener(v -> { // 先校验是否选择了图片 if (selectedImagePath == null || selectedImagePath.isEmpty()) { status.setText("请先选择图片!"); return; } try (Socket sock = new Socket("192.168.0.12", 8000)) { System.out.println("Connecting..."); File myFile = new File(selectedImagePath); long fileSize = myFile.length(); OutputStream os = sock.getOutputStream(); // 先发送文件大小(用8字节的long类型,支持大文件) os.write((byte) (fileSize >> 56)); os.write((byte) (fileSize >> 48)); os.write((byte) (fileSize >> 40)); os.write((byte) (fileSize >> 32)); os.write((byte) (fileSize >> 24)); os.write((byte) (fileSize >> 16)); os.write((byte) (fileSize >> 8)); os.write((byte) (fileSize)); // 用小缓冲区循环读取发送,避免内存溢出 try (FileInputStream fis = new FileInputStream(myFile); BufferedInputStream bis = new BufferedInputStream(fis)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = bis.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } os.flush(); status.setText("图片发送成功!"); } } catch (UnknownHostException e) { e.printStackTrace(); status.setText("无法连接到服务器!"); } catch (IOException e) { e.printStackTrace(); status.setText("发送失败:" + e.getMessage()); } }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) { Uri selectedImageUri = data.getData(); selectedImagePath = getPath(selectedImageUri); TextView path = (TextView) findViewById(R.id.tvPath); path.setText("Image Path : " + selectedImagePath); img.setImageURI(selectedImageUri); } } public String getPath(Uri uri) { String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = managedQuery(uri, projection, null, null, null); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path = cursor.getString(column_index); cursor.close(); // 关闭游标避免内存泄漏 return path; } }
修复后的服务器代码
服务器需要先接收文件大小,再根据大小循环接收完整的文件内容:
import java.io.*; import java.net.*; public class FileServer { public static void main(String[] args) throws IOException { ServerSocket servsock = new ServerSocket(8000); System.out.println("服务器启动,等待连接..."); while (true) { try (Socket sock = servsock.accept()) { System.out.println("已接受连接:" + sock); long start = System.currentTimeMillis(); InputStream is = sock.getInputStream(); // 先接收文件大小 byte[] sizeBuffer = new byte[8]; if (is.read(sizeBuffer) != 8) { System.out.println("无法读取文件大小,断开连接"); continue; } long fileSize = ((long) sizeBuffer[0] << 56) | ((long) (sizeBuffer[1] & 0xFF) << 48) | ((long) (sizeBuffer[2] & 0xFF) << 40) | ((long) (sizeBuffer[3] & 0xFF) << 32) | ((long) (sizeBuffer[4] & 0xFF) << 24) | ((long) (sizeBuffer[5] & 0xFF) << 16) | ((long) (sizeBuffer[6] & 0xFF) << 8) | ((long) (sizeBuffer[7] & 0xFF)); System.out.println("即将接收文件大小:" + fileSize + "字节"); // 接收文件内容 try (FileOutputStream fos = new FileOutputStream("C:\\Users\\Gustavo\\Desktop\\WebOffice.jpg"); BufferedOutputStream bos = new BufferedOutputStream(fos)) { byte[] buffer = new byte[4096]; long bytesReceived = 0; int bytesRead; while (bytesReceived < fileSize && (bytesRead = is.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); bytesReceived += bytesRead; } bos.flush(); long end = System.currentTimeMillis(); System.out.println("文件接收完成,耗时:" + (end - start) + "ms"); System.out.println("实际接收字节数:" + bytesReceived); } } catch (IOException e) { e.printStackTrace(); System.out.println("连接出现错误:" + e.getMessage()); } } } }
额外注意事项
- Android权限适配:Android 10及以上版本,
MediaStore.Images.Media.DATA可能无法获取真实路径,建议改用ContentResolver.openInputStream()读取图片,避免路径获取失败。 - 内存优化:不要一次性把整个文件读到内存中(比如原代码的
new byte[(int)myFile.length()]),大图片会直接导致内存溢出,用小缓冲区循环读取更安全。 - 异常调试:代码里增加了更多的提示信息,方便你快速定位问题,比如客户端的图片选择校验、服务器的接收状态打印。
内容的提问来源于stack exchange,提问作者Rama
相关产品推荐
相关产品推荐

