You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:48:14