Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a WebSocket Server in java?

I'm working on setting up my first websocket server for a communications app. I can't seem to figure out how websockets are implemented in Java.

I've tried, unsuccessfully, creating an annotation based Endpoint, but I'm not sure where the client info is going to come through. This is basically the gist of my code, without going into mundane details.

I'm trying to make the class MessageHelper deal with the websocket info transfer, I just can't grasp how to actually get the transfer there.

class MainServer implements Runnable {
// VARIABLES
    ServerSocket serverSocket = null;
    int port;
// CONSTRUCTORS
    MainServer(int p) {
        this.port = p;
    }
// METHODS
    public void run() {
        openServerSocket();
        while(!isStopped()){
            try{
                clientSocket = serverSocket.accept();
            } catch(IOException e) {
                // Do something
            }
            new Thread(new MainThread(clientSocket)).start();
        }
    }
}

// Other methods below.
public class MainThread {

    final Socket socket;


    MainThread(Socket s) {
        this.socket = s;
    }

    public void run() {
        try{
            BufferedReader br = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));

            String input = br.readLine(), read = br.readLine();
            while(!input.isEmpty()) {
                read += "\n";
                read += input;
                input = br.readLine();
            }

            /**
            *  Everything works fine, I'm just not sure where to go
            * from here. I tried creating MessageHelper into the java
            * websocket implementation using annotations but it did not 
            * accept input from the client after the handshake was
            * made. My client would send something but it would just 
            * give and EOFException.
            **/
            if(websocketHandshakeRequest(read)) {
                MessageHelper messageHelper = 
                    new MessageHelper(this.socket);
            } else {
                // Do something
            }
        } catch(Exception e) {
            // Do something.
        }
    }
}
like image 745
Kyler Avatar asked Sep 13 '19 10:09

Kyler


1 Answers

Don't be confused about the name WebSocket. A TCP socket and a WebSocket are entirely different kind of "sockets".

In Java you use a ServerSocket for TCP sockets. TCP is a transport layer protocol used to implement application layer protocols like POP3 and HTTP.

WebSocket is a HTTP/1.1 protocol upgrade commonly used in web servers and web browsers. You cannot use a ServerSocket for the WebSocket protocol, at least not so straight forward as you might think. First, you have to implement the HTTP/1.1 protocol and then the WebSocket protocol on top of that.

In the Java world you can use web servers like Tomcat or Jetty which provide WebSocket implementations and a high level Java API. This API is part of the Jave Enterprise Edition (JEE). See also the Jave EE 7 Tutorial - Chapter 18 Java API for WebSocket.

E.g. Jetty is a lightweight JEE web server which can be embedded in your application or run as a stand-alone server. See Jetty Development Guide - Chapter 26. WebSocket Introduction.

So in a Java web application running in a WebSocket enabled JEE web server like Jetty you can implement a server side WebSocket as follows:

package com.example.websocket;

import org.apache.log4j.Logger;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/toUpper")
public class ToUpperWebsocket {

  private static final Logger LOGGER = Logger.getLogger(ToUpperWebsocket.class);

  @OnOpen
  public void onOpen(Session session) {
    LOGGER.debug(String.format("WebSocket opened: %s", session.getId()));
  }

  @OnMessage
  public void onMessage(String txt, Session session) throws IOException {
    LOGGER.debug(String.format("Message received: %s", txt));
    session.getBasicRemote().sendText(txt.toUpperCase());
  }

  @OnClose
  public void onClose(CloseReason reason, Session session) {
    LOGGER.debug(String.format("Closing a WebSocket (%s) due to %s", session.getId(), reason.getReasonPhrase()));
  }

  @OnError
  public void onError(Session session, Throwable t) {
    LOGGER.error(String.format("Error in WebSocket session %s%n", session == null ? "null" : session.getId()), t);
  }
}

You register your class as a WebSocket handler for the specific path with the @ServerEndpoint annotation. Your WebSocket URL is then ws://host:port/context/toUpper or wss://host:port/context/toUpper for HTTPS connections.

Edit: Here is a very simplistic HTML page to demonstrate the client side connection to the above WebSocket. This page is served by the same webserver as the WebSocket. The webapplication containing the WebSocket is deployed at context "websocket" on localhost port 7777.

    <html>
    <body>
    <h2>WebSocket Test</h2>
    <div>
    <input type="text" id="input" />
    </div>
    <div>
    <input type="button" id="connectBtn" value="CONNECT" onclick="connect()" />
    <input type="button" id="sendBtn" value="SEND" onclick="send()" disable="true" />
    </div>
    <div id="output">
    <h2>Output</h2>
    </div>
    </body>
    <script type="text/javascript">
    var webSocket;
    var output = document.getElementById("output");
    var connectBtn = document.getElementById("connectBtn");
    var sendBtn = document.getElementById("sendBtn");
    var wsUrl = (location.protocol == "https:" ? "wss://" : "ws://") + location.hostname + (location.port ? ':'+location.port: '') + "/websocket/toUpper";

    function connect() {
      // open the connection if one does not exist
      if (webSocket !== undefined
        && webSocket.readyState !== WebSocket.CLOSED) {
        return;
      }

      updateOutput("Trying to establish a WebSocket connection to <code>" + wsUrl + "</code>");

      // Create a websocket
      webSocket = new WebSocket(wsUrl);

      webSocket.onopen = function(event) {
        updateOutput("Connected!");
        connectBtn.disabled = true;
        sendBtn.disabled = false;
      };

      webSocket.onmessage = function(event) {
        updateOutput(event.data);
      };

      webSocket.onclose = function(event) {
        updateOutput("Connection Closed");
        connectBtn.disabled = false;
        sendBtn.disabled = true;
      };
    }

    function send() {
      var text = document.getElementById("input").value;
      webSocket.send(text);
    }

    function closeSocket() {
      webSocket.close();
    }

    function updateOutput(text) {
      output.innerHTML += "<br/>" + text;
    }
    </script>
    </html>

Sample WebSocket webpage rendered in Firefox

like image 153
vanje Avatar answered Sep 29 '22 16:09

vanje