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