The server code below which I blogged about during it progress is now working and ready for me to move on to other little projects.
When the server receives a header of “GSIF” (Get Server Image File) it also reads the requested width and height of the image. It will load the image in, scale it to the requested size, compress it as a PNG and then send the data to the client.
EDIT: This would have been much better if I’d have used DataInputStream and DataOutputStream instead of reading bytes.
Whilst the server is running its output is like this:
WLGfx Home Server - Starting up Shutdown hook attached TCP Server thread started... police pulled me over.jpg icy stare from wife.jpg quiche.jpg shopping carts.jpg liverpool shoe shop burgled.jpg camo-truck.jpg monkey cpr.jpg cure for shits.jpg irish speed sign.jpg hold in a fart.jpg satisfaction erection.jpg 6 to 8 beers a day.jpg beer and scrambled eggs.jpg toilet paper.jpg ass signals batman.jpg moby stairlift.jpg it might rain today.jpg hulk jerking you off.jpg prick with a fork.jpg sunday lunches.jpg dog saves baby.jpg evolution started this way.jpg less make up.jpg ham football.jpg poor blind dog.jpg turning the heat off.jpg drink with me.jpg happy hour about to end.jpg rock bottom.jpg astronaught carrot.jpg seeing eye dogs.jpg david fell decorating.jpg two piece bathing suit.jpg key cleaner.jpg balls-stuck-in-door.jpg jehovas never returned.jpg ride me all day.jpg TCP Incoming socket handler Read 4 bytes from socket Got header: GSIF Read 2 bytes from socket Read 2 bytes from socket Requested dimensions: 896,431 TCP Incoming socket handler Read 4 bytes from socket Got header: GSIF Read 2 bytes from socket Read 2 bytes from socket Requested dimensions: 896,431 TCP Incoming socket handler Read 4 bytes from socket
The TCPServer class code:
package com.wlgfx.server.home; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.imageio.ImageIO; public class TCPServer implements Runnable { private Thread thread = null; boolean stopServer; Random random; List image_files = null; public TCPServer() { start(); } public void start() { if (thread == null) { stopServer = false; thread = new Thread(this); thread.start(); } } public void stop() { stopServer = true; while (thread != null) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } private void initialise_image_list() { random = new Random(); image_files = new ArrayList(); File folder = new File(Global.dir_pictures); File[] files = folder.listFiles(); for (File file : files) { if (file.isFile()) { image_files.add(file.getName()); System.out.println(file.getName()); } } } @Override public void run() { System.out.println("TCP Server thread started..."); initialise_image_list(); ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(Global.tcp_port); serverSocket.setSoTimeout(1000); while (stopServer == false) { try { Socket socket = serverSocket.accept(); if (socket != null) new TCPSocketHandler(socket); } catch (InterruptedIOException e) { //System.out.println("Timed out socket"); } } } catch (IOException e) { e.printStackTrace(); } finally { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } thread = null; System.out.println("TCP Thread finished..."); } private class TCPSocketHandler implements Runnable { Thread thread = null; Socket socket = null; byte[] header = new byte[4]; public TCPSocketHandler(Socket insocket) { socket = insocket; thread = new Thread(this); thread.start(); } @Override public void run() { System.out.println("TCP Incoming socket handler"); try { readSocketBytes(socket, header, 4); String head = new String(header, StandardCharsets.UTF_8); System.out.println("Got header: " + head); if (head.equals("GSIF")) { // get server image file process_gsif(socket); } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.getOutputStream().flush(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } private void readSocketBytes(Socket socket, byte[] bytes, int len) throws IOException { int position = 0; InputStream in = socket.getInputStream(); while (position < len) { int count = in.read(bytes, position, len - position); if (count >= 0) position += count; else throw new IOException(); } System.out.println("Read " + position + " bytes from socket"); } private void writeSocketBytes(Socket socket, byte[] bytes) throws IOException { OutputStream out = socket.getOutputStream(); byte[] length_data = new byte[4]; int size = bytes.length; length_data[0] = (byte) ((size >> 24) & 255); length_data[1] = (byte) ((size >> 16) & 255); length_data[2] = (byte) ((size >> 8) & 255); length_data[3] = (byte) ((size) & 255); out.write(length_data); out.write(bytes); } private int readSocketInt16(Socket socket) throws IOException { byte[] data = new byte[2]; readSocketBytes(socket, data, 2); return (data[0] & 255) << 8 | (data[1] & 255); } private void process_gsif(Socket socket) { // header: GSIF // short: width // short: height // returns... // int32: length // byte array: png image data // get random image, load it in, scale to requested size, // compress as png, and send it to client BufferedImage image = null; String image_file = image_files.get(random.nextInt(image_files.size())); try { int width = readSocketInt16(socket); int height = readSocketInt16(socket); System.out.println("Requested dimensions: " + width + "," + height); image = getScaledImage(ImageIO.read(new File(Global.dir_pictures + image_file)), width, height); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); baos.flush(); byte[] data = baos.toByteArray(); writeSocketBytes(socket, data); } catch (IOException e) { e.printStackTrace(); } } private BufferedImage getScaledImage(BufferedImage image, int width, int height) { double scaleX = (double)width / image.getWidth(); double scaleY = (double)height / image.getHeight(); AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); AffineTransformOp biliniearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); return biliniearScaleOp.filter(image, new BufferedImage(width, height, image.getType())); } } }
The android ASync class to fetch a random image:
package com.uiprojtest.wlgfx.uiprojecttest; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import android.widget.ImageView; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; /** * Created by wlgfx on 02/09/16. */ public class GetImageFromServer extends AsyncTask<Void, Void, Bitmap> { private final String tag = "WLGFX-AsyncBitmap"; private Context context; private ImageView imageView; private int width; private int height; GetImageFromServer(Context ctx, ImageView view) { context = ctx; imageView = view; } @Override protected void onPreExecute() { super.onPreExecute(); imageView.setImageBitmap(null); width = imageView.getWidth(); height = imageView.getHeight(); } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); imageView.setImageBitmap(bitmap); Log.d(tag, "Bitmap: " + "(" + bitmap.getWidth() + "," + bitmap.getHeight() + ")" + " " + bitmap.getByteCount()); } /* commented out now as it was the original test @Override protected Bitmap doInBackground(Void... voids) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // just get image dimensions BitmapFactory.decodeResource(context.getResources(), R.drawable.bike, options); options.inSampleSize = Math.max(options.outWidth / width, options.outHeight / height); options.inJustDecodeBounds = false; // let's do this now options.inPreferredConfig = Bitmap.Config.RGB_565; // reduced memory footprint Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.bike, options); // could rescale here to exact required size but no need for this return bitmap; }*/ @Override protected Bitmap doInBackground(Void... voids) { String head = "GSIF"; byte[] header_data = head.getBytes(); try { InetAddress address = InetAddress.getByName(Global.server_host); Socket socket = new Socket(address, Global.tcp_port); OutputStream out = socket.getOutputStream(); out.write(header_data); byte[] dim = new byte[2]; dim[0] = (byte)(width >> 8); dim[1] = (byte)width; out.write(dim); dim[0] = (byte)(height >> 8); dim[1] = (byte)(height); out.write(dim); byte[] length = new byte[4]; readSocketBytes(socket, length); int size = (length[0] & 255) << 24 | (length[1] & 255) << 16 | (length[2] & 255)<< 8 | length[3] & 255; byte[] image_data = new byte[size]; readSocketBytes(socket, image_data); Bitmap bitmap = BitmapFactory.decodeByteArray(image_data, 0, size); socket.close(); return bitmap; } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private void readSocketBytes(Socket socket, byte[] data) throws IOException { int length = data.length; InputStream in = socket.getInputStream(); int position = 0; while (position < length) { int count = in.read(data, position, length - position); if (count > 0) position += count; else throw new IOException(); } } }