Java TCP客户端如何解析服务器发来的特定格式图片字节数据?
Fixing Binary Image Reception in Your Java TCP Client
Hey Berk, let's get this image handling sorted out! The main issue with your current code is that you're using BufferedReader—a character-focused stream—to read binary image data. This will corrupt your images because it converts bytes to characters, which doesn't work for raw binary content like images. We need to switch to byte-based streams instead.
Here's a step-by-step solution tailored to your server's message format:
Key Issues to Address
BufferedReaderis for text, not binary data. UseDataInputStreamto read raw bytes directly from the socket.- Your server sends messages in the format:
ISND(4 ASCII bytes) + 3-byte size integer + image data (size bytes). We need to read each component precisely. - You need to handle 3 such image messages before sending the
EXITcommand.
Revised Code with Image Handling
Let's update your main method to properly read and save the images:
import java.io.*; import java.net.Socket; import java.util.logging.Level; import java.util.logging.Logger; public class ImageLabeler { public static void main(String[] args) { if (args.length < 2) { System.out.println("Usage: java ImageLabeler <IP> <Port>"); System.exit(1); } String IP = args[0]; int port = Integer.parseInt(args[1]); System.out.printf("Connecting to %s:%d%n", IP, port); // Use try-with-resources to auto-close sockets/streams try (Socket clientSocket = new Socket(IP, port); DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream()); BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); DataInputStream imageIn = new DataInputStream(clientSocket.getInputStream())) { System.out.println("Connection success!\n"); // Send authentication commands (text-based, so PrintWriter is okay here) PrintWriter writer = new PrintWriter(outToServer, true); writer.println("USER bilkentstu"); String serverResponse = inFromServer.readLine(); System.out.println("Server response: " + serverResponse + "\n"); writer.println("PASS cs421f2019"); serverResponse = inFromServer.readLine(); System.out.println("Server response: " + serverResponse + "\n"); // Request images writer.println("IGET"); System.out.println("Requested images, waiting for response...\n"); // Process 3 image messages for (int imgNum = 1; imgNum <= 3; imgNum++) { // Step 1: Read and verify the "ISND" header byte[] headerBytes = new byte[4]; imageIn.readFully(headerBytes); // Ensures we read all 4 bytes String header = new String(headerBytes, "ASCII"); if (!"ISND".equals(header)) { System.err.printf("Invalid header for image %d: %s%n", imgNum, header); break; } System.out.printf("Received valid header for image %d%n", imgNum); // Step 2: Read 3-byte size integer (assuming big-endian/network byte order) byte[] sizeBytes = new byte[3]; imageIn.readFully(sizeBytes); int imageSize = ((sizeBytes[0] & 0xFF) << 16) | ((sizeBytes[1] & 0xFF) << 8) | (sizeBytes[2] & 0xFF); System.out.printf("Image %d size: %d bytes%n", imgNum, imageSize); // Step 3: Read the image data byte[] imageData = new byte[imageSize]; imageIn.readFully(imageData); System.out.printf("Finished reading image %d data%n", imgNum); // Step 4: Save image to file String fileName = String.format("image_%d.jpg", imgNum); // Adjust extension if needed try (FileOutputStream fos = new FileOutputStream(fileName)) { fos.write(imageData); System.out.printf("Saved image to %s%n", fileName); } catch (IOException e) { System.err.printf("Failed to save image %d: %s%n", imgNum, e.getMessage()); } } // Send exit command writer.println("EXIT"); System.out.println("\nSent EXIT command, closing connection."); } catch (IOException ex) { Logger.getLogger(ImageLabeler.class.getName()).log(Level.SEVERE, "Connection or communication error", ex); System.exit(1); } } }
Important Notes
readFully()vs.read(): We usereadFully()to ensure we read exactly the number of bytes we need—this avoids partial reads which are common in TCP streams.- Byte Order for Size: The code assumes the 3-byte size is in big-endian (network byte order). If your server uses little-endian, you'll need to reverse the byte order when calculating
imageSize:int imageSize = ((sizeBytes[2] & 0xFF) << 16) | ((sizeBytes[1] & 0xFF) << 8) | (sizeBytes[0] & 0xFF); - Image File Extension: Adjust the file extension (
.jpg,.png, etc.) to match the actual format of images sent by the server. - Try-with-Resources: We use try-with-resources blocks to automatically close sockets and streams, which prevents resource leaks.
- Authentication: The text-based authentication commands (
USER,PASS) still usePrintWriterandBufferedReadersince those are text messages—this is fine, as long as we switch toDataInputStreamfor the binary image data.
Why This Works
DataInputStreamreads raw bytes directly from the socket, preserving the integrity of the image data.- We explicitly read each component of the server's message (header, size, image data) in the exact format specified, ensuring we don't mix up or corrupt data.
- Saving the byte array directly to a file with
FileOutputStreamwrites the raw image data correctly, so you'll get valid image files.
内容的提问来源于stack exchange,提问作者Berk Erzin
相关产品推荐
相关产品推荐

