How to Implement a Java WebSocket Server for Image Transmission with Jetty

In the previous articles, I shared how to implement a .Net WebSocket server with SuperWebSocket. Today, I’d like to continue the series WebSocket: How-to, talking about how to implement a WebSocket server in Java.

What is Jetty?

“Jetty provides a Web server andjavax.servlet container, plus support forSPDY, WebSocket, OSGi, JMX, JNDI, JAASand many other integrations.” from http://www.eclipse.org/jetty/

Quickly Setup a Java WebSocket Server

Create a class WSHandler which extends the class WebSocketHandler:

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

@WebSocket
public class WSHandler extends WebSocketHandler {

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
    }

    @OnWebSocketError
    public void onError(Throwable t) {
    }

    @OnWebSocketConnect
    public void onConnect(Session session) {
    }

    @OnWebSocketMessage
    public void onMessage(String message) {
    }

	@Override
	public void configure(WebSocketServletFactory factory) {
		// TODO Auto-generated method stub
		factory.register(WSHandler.class);
	}
}

Start the server with a port and a handler:

    public static void main(String[] args) throws Exception {
        Server server = new Server(2014);
        server.setHandler(new WSHandler());
        server.setStopTimeout(0);
        server.start();
        server.join();
    }

Done.

JavaScript Client for WebSocket Connection

We can create a simple web client for testing.

Index.htm:

<!DOCTYPE html>
<html>
    <body>
        <script src="websocket.js"></script>
    </body>
</html>

Websocket.js:

var ws = new WebSocket("ws://127.0.0.1:2014/");

ws.onopen = function() {
    alert("Opened");
    ws.send("I'm client");
};

ws.onmessage = function (evt) { 
};

ws.onclose = function() {
    alert("Closed");
};

ws.onerror = function(err) {
    alert("Error: " + err);
};

Run the app and check the message.

Image Transmission Between WebSocket Server & Web Clients

What I’m going to do:

  • Fetch an image from the WebSocket server by clicking a HTML button on Web clients.js load image

 

  • Send an image from the Java WebSocket server to all connected Web clients by clicking the Swing button Send.java send image

 

How to improve the source code?

Clients fetch images:

  • On the client side, we need to add a button and an image in index.htm:
    <!DOCTYPE html>
    <html>
        <body>
            <h1>WebSocket Image Display</h1>
            <input type="button" id="button" value="image" ><br>
            <img id="image"></<img>
            <script src="websocket.js"></script>
        </body>
    </html>
  •  In websocket.js, use the following code to fetch and display the image:
    ws.binaryType = "arraybuffer";
    var button = document.getElementById("button");
    button.onclick = function() {
    	ws.send("image"); // send the fetch request
    };
    ws.onmessage = function (evt) { // display the image
        var bytes = new Uint8Array(evt.data);
        var data = "";
        var len = bytes.byteLength;
        for (var i = 0; i < len; ++i) {
        	data += String.fromCharCode(bytes[i]);
        }
        var img = document.getElementById("image");
        img.src = "data:image/png;base64,"+window.btoa(data);
    };
  • Once a message received, the server will load an image and send it to Clients:
    public void onMessage(String message) {
            System.out.println("Message: " + message);
            if (message.equals("image")) {
            	System.out.println("session: " + mSession);
            	if (mSession != null) {
    				try {
    					File f = new File("image\\github.jpg");
    					BufferedImage bi = ImageIO.read(f);
    					ByteArrayOutputStream out = new ByteArrayOutputStream();
    					ImageIO.write(bi, "png", out);
    					ByteBuffer byteBuffer = ByteBuffer.wrap(out.toByteArray());
    					mSession.getRemote().sendBytes(byteBuffer);
    					out.close();
    					byteBuffer.clear();
    
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
            	}
            }
        }

Server  sends images:

  • On the server side, there are two buttons: one for loading images from the local disk, and the other for sending images to Web clients. Here is the UI code in Java Swing:
    package com.ui;
    
    import java.awt.BorderLayout;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.filechooser.FileNameExtensionFilter;
    
    import com.server.WSHandler;
    import com.server.WebSocketServer;
    
    public class UIMain extends JPanel
                                 implements ActionListener {
        private JButton mLoad, mSend;
        private JFileChooser mFileChooser;
        private JLabel mImage;
        private byte[] mData;
        private WebSocketServer mWebSocketServer;
    
        public UIMain() {
            super(new BorderLayout());
    
            //Create a file chooser
            mFileChooser = new JFileChooser();
            FileNameExtensionFilter filter = new FileNameExtensionFilter(
                    ".png.jpg", "png","jpg");
            mFileChooser.setFileFilter(filter);
            mLoad = new JButton("Load");
            mLoad.addActionListener(this);
    
            mSend = new JButton("Send");
            mSend.addActionListener(this);
            mSend.setEnabled(false);
    
            // button panel
            JPanel buttonPanel = new JPanel(); 
            buttonPanel.add(mLoad);
    		buttonPanel.add(mSend);
            add(buttonPanel, BorderLayout.PAGE_START);
    
            // image panel
    		JPanel imageViewer = new JPanel();
    		mImage = new JLabel();
    		mImage.setSize(480, 640);
    		imageViewer.add(mImage);
    		add(imageViewer, BorderLayout.CENTER);
    
    		// WebSocketServer
    		mWebSocketServer = new WebSocketServer();
    		mWebSocketServer.start();
        }
    
        @Override
    	public void actionPerformed(ActionEvent e) {
    
        }
    
        private static void createAndShowGUI() {
            //Create and set up the window.
            JFrame frame = new JFrame("WebSocket Demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            //Add content to the window.
            frame.add(new UIMain());
    
            //Display the window.
            frame.pack();
            frame.setVisible(true);
            frame.setResizable(false);
            frame.setSize(480, 700);
    
            double width = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
            double height = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
            int frameWidth = frame.getWidth();
            int frameHeight = frame.getHeight();
            frame.setLocation((int)(width - frameWidth) / 2, (int)(height - frameHeight) / 2);
        }
    
        public static void main(String[] args) {
    
            SwingUtilities.invokeLater(new Runnable() {
                @Override
    			public void run() {
                    UIManager.put("swing.boldMetal", Boolean.FALSE); 
                    createAndShowGUI();
                }
            });
        }
    }
  • Note: do not start the WebSocket server in UI thread, otherwise the UI will be blocked. So, you have to launch the server in a worker thread:
    package com.server;
    
    import org.eclipse.jetty.server.Server;
    
    public class WebSocketServer extends Thread{
    
        @Override
        public void run() {
        	// TODO Auto-generated method stub
        	super.run();
    
            try {
            	Server server = new Server(2014);
            	server.setHandler(new WSHandler());
            	server.setStopTimeout(0);
            	server.start();
            	server.join();
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
        }
    }
  • The events for loading and sending image:
    public void sendImage(byte[] data) {
    		if (mSession == null)
    			return;
    
    		try {        	
    			ByteBuffer byteBuffer = ByteBuffer.wrap(data);
                mSession.getRemote().sendBytes(byteBuffer);
                byteBuffer.clear();
            } catch (IOException e) {
                e.printStackTrace();
            }
    	}
    
    @Override
    	public void actionPerformed(ActionEvent e) {
    
            if (e.getSource() == mLoad) {
    
                int returnVal = mFileChooser.showOpenDialog(UIMain.this);
    
                if (returnVal == JFileChooser.APPROVE_OPTION) {
                    File file = mFileChooser.getSelectedFile();     
    
                    // load image data to byte array
                    try {        	
                    	BufferedImage bi = ImageIO.read(file);
                		ByteArrayOutputStream out = new ByteArrayOutputStream();
                		ImageIO.write(bi, "png", out);
                		mData = out.toByteArray();
                		out.close();
                    } catch (IOException exception) {
                    	exception.printStackTrace();
                    }
    
                    mImage.setIcon(new ImageIcon(mData));
                    mSend.setEnabled(true);
                }
            } 
            else if (e.getSource() == mSend) {
            	ArrayList<WSHandler> sessions = WSHandler.getAllSessions();
            	for (WSHandler session : sessions) {
            		session.sendImage(mData);
            	}
            	mSend.setEnabled(false);
            }
        }

That’s all. You can try to make your hands dirty now.

Source Code

JavaWebSocket