Establishing WebSocket connection with Java server and Javascript client

11,021

Solution 1

I think, it is because you are using the Socket Package on the Java Server Side and the WebSocket API on the Client Side. Your idea is really good but the wrong technology. Keep the WebSocket on the Client Side (Javascript) becaue you don't have lots of other possibilities, but try JWebSocket on the Server side (Java). In Fact WebSocket is using TCP/IP but its own communication protocol over TCP/IP. The Java Socket Package is purely TCP/IP. Re-write your server with JWebSocket, all details about JWebSocket can be found at: http://jwebsocket.org/. I hope my answer will help you.

Solution 2

you must specify end of return packet with "\r\n\r\n"

  ret = "HTTP/1.1 101 Switching Protocols\r\n";
   ret+="Upgrade: websocket\r\n";
   ret+="Connection: Upgrade\r\n";
   ret+="Sec-WebSocket-Accept: "+returnBase + "\r\n\r\n";

and for create accept key i use

public class WSKeyGenerator {
    private final static String MAGIC_KEY =
            "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    public static String getKey(String strWebSocketKey) throws
            NoSuchAlgorithmException {
        strWebSocketKey += MAGIC_KEY;
        MessageDigest shaMD = MessageDigest.getInstance("SHA-1");
        shaMD.reset();
        shaMD.update(strWebSocketKey.getBytes());
        byte messageDigest[] = shaMD.digest();
        BASE64Encoder b64 = new BASE64Encoder();
        return b64.encode(messageDigest);
    }
}

I recommend that use the http://websocket.org/echo.html to check the server's websocket functionality

Share:
11,021

Related videos on Youtube

Yuri Heupa
Author by

Yuri Heupa

Software Engineer

Updated on September 15, 2022

Comments

  • Yuri Heupa
    Yuri Heupa over 1 year

    I'm trying to implement WebSockets with a Javascript-based client and a Java-based server. I think I've done all the correct steps, but for an unknown reason, I can't establish the connection with both.

    When the server socket receives a connection, it handles to form a websocket-accept response, and it sends back to the client, but the connection in the client socket instantly close, weird that there's no handshake problem.

    Does anyone have an idea what might be the problem?

    Here's my server code implemented in java:

    package server;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    import java.util.List;
    
    import server.message.Message;
    import server.message.SpeakMessage;
    
    
    public class Server implements ConnectionListener {
        private static final int PORT = 1509;
        private MessageDispatcher dispatcher = new MessageDispatcher();
        private List<ConnectionManager> clients = new ArrayList<>();
    
        public void listen() {
            try (ServerSocket server = new ServerSocket(PORT)) {
                System.out.printf("Listening on port %d...%n", PORT);
                while (true) {
                    System.out.println("Waiting for connection...");
                    Socket client = server.accept();
                    System.out.println("Incoming connection - Attempting to establish connection...");
                    ConnectionManager manager = new ConnectionManager(client, dispatcher, this);
                    manager.start();
                }
            } catch (IOException e) {
                System.out.println("Unable to start server");
                e.printStackTrace();
            }
            System.exit(0);
        }
    
        public void execute() {
            try {
                while (true) {
                    if (dispatcher.isEmpty()) {
                        Thread.sleep(100);
                        continue;
                    }
                    Message msg = dispatcher.read();
                    if (msg instanceof SpeakMessage)
                        broadcast(MessageEncoder.spoke(((SpeakMessage) msg).getText()));
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
    
        public static void main(String[] args) {
            final Server server = new Server();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    server.listen();
                }
            }).start();
            server.execute();
        }
    
        public synchronized void broadcast(byte[] message) {
            for (ConnectionManager client : clients) {
                client.send(message);
            }
        }
    
        @Override
        public synchronized void clientConnected(ConnectionManager who) {
            clients.add(who);
            System.out.println("Connected client " + clients.size());
        }
    
        @Override
        public synchronized void clientDisconnected(ConnectionManager who) {
            clients.remove(who);
        }
    }
    

    Heres subclass ConnectionManager of server:

    package server;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.InvocationTargetException;
    import java.net.Socket;
    import java.security.MessageDigest;
    import java.util.Properties;
    
    import server.message.HandshakeMessage;
    import server.message.Message;
    
    
    public class ConnectionManager {
        private static final int CLIENT_VERSION = 1;
        private Socket socket;
        private MessageDecoder decoder = new MessageDecoder();
        private MessageDispatcher dispatcher;
        private ConnectionListener listener;
    
        public ConnectionManager(Socket connection, MessageDispatcher dispatcher, ConnectionListener listener) {
            socket = connection;
            this.dispatcher = dispatcher;
            this.listener = listener;
        }
    
        public void start() {
            Thread t = new Thread(new ChannelReader());
            t.setName("Client thread");
            t.setDaemon(true);
            t.start();
        }
    
        public void send(byte[] data) {
            if (socket == null)
                return;
    
            try {
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                dos.write(data);
                dos.flush();
            } catch (IOException e) {
                disconnect("Client closed the connection");
            }
        }
    
        private class ChannelReader implements Runnable {
            private boolean accepted = false;
            private String ret = null;
    
            @Override
            public void run() {
                try {
                    DataInputStream in = new DataInputStream(socket.getInputStream());
                    while (socket != null && socket.isConnected()) {
                        int len = in.readShort();
                        if (len < 0) {
                            disconnect("Invalid message length.");
                        }
    
                        String s;
                        readLine(in);
                        Properties props = new Properties();
                        while((s=readLine(in)) != null && !s.equals("")) {
                            String[] q = s.split(": ");
                            props.put(q[0], q[1]);
                        }
    
                        if(props.get("Upgrade").equals("websocket") && props.get("Sec-WebSocket-Version").equals("13")) { // check if is websocket 8
                            String key = (String) props.get("Sec-WebSocket-Key");
                            String r = key + "" + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // magic key
                            MessageDigest md = MessageDigest.getInstance("SHA-1");
                            md.reset();
                            md.update(r.getBytes());
                            byte[] sha1hash = md.digest();
    
    
                            String returnBase = base64(sha1hash);
    
    
                            ret = "HTTP/1.1 101 Switching Protocols\r\n";
                                ret+="Upgrade: websocket\r\n";
                                ret+="Connection: Upgrade\r\n";
                                ret+="Sec-WebSocket-Accept: "+returnBase;
    
                        } else {
                            disconnect("Client got wrong version of websocket");
                        }
    
                        Message msg = decoder.decode((String) props.get("Sec-WebSocket-Protocol"));
    
                        if (!accepted) {
                            doHandshake(msg);
                        } else if (dispatcher != null) {
                            dispatcher.dispatch(msg);
                        }
                    }
                } catch (Exception e) {
                    disconnect(e.getMessage());
                    e.printStackTrace();
                }
            }
    
            private void doHandshake(Message msg) {
                if (!(msg instanceof HandshakeMessage)) {
                    disconnect("Missing handshake message");
                    return;
                }
                HandshakeMessage handshake = (HandshakeMessage) msg;
                if (handshake.getVersion() != CLIENT_VERSION) {
                    disconnect("Client failed in handshake.");
                    return;
                }
                send(ret.getBytes());
                accepted = true;
                listener.clientConnected(ConnectionManager.this);
            }   
    
            private String base64(byte[] input) throws ClassNotFoundException, 
            SecurityException, NoSuchMethodException, IllegalArgumentException, 
            IllegalAccessException, InvocationTargetException, InstantiationException {
                Class<?> c = Class.forName("sun.misc.BASE64Encoder");
                java.lang.reflect.Method m = c.getMethod("encode", new Class<?>[]{byte[].class});
                String s = (String) m.invoke(c.newInstance(), input);
                return s;
            }
    
            private String readLine(InputStream in) {
                try{
                    String line = "";
                    int pread;
                    int read = 0;
                    while(true) {
                        pread = read;
                        read = in.read();
                        if(read!=13&&read!=10)
                            line += (char) read;
                        if(pread==13&&read==10) break;
                    }
                    return line;
                }catch(IOException ex){
    
                }
                return null;
            }
    
        }
    
        public synchronized void disconnect(String message) {
            System.err.println(message);
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
    
                }
            }
            socket = null;
            listener.clientDisconnected(ConnectionManager.this);
        }
    }
    

    And the MessageDispatcher:

    package server;
    
    import java.util.Queue;
    import java.util.concurrent.LinkedBlockingDeque;
    
    import server.message.Message;
    
    
    public class MessageDispatcher {
        Queue<Message> messageQueue = new LinkedBlockingDeque<>();
    
        public void dispatch(Message message) {
            messageQueue.offer(message);
        }
    
        public Message read() {
            return messageQueue.poll();
        }
    
        public boolean isEmpty() {
            return messageQueue.isEmpty();
        }
    }
    

    And heres my client code implemented in javascript:

    var canvas, // Canvas DOM element
        ctx,    // Canvas rendering context
        socket; // Socket connection
    
    function init() {
        // Initialise the canvas
        canvas = document.getElementById("gameCanvas");
        ctx = canvas.getContext("2d");
        // Maximise the canvas
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    
        // Initialise socket connection
        if (window.WebSocket) { 
            socket = new WebSocket("ws://localhost:1509/", ["1", "YURI"]);
            socket.onopen = onSocketConnected();
            socket.onclose = onSocketDisconnect();
            socket.onmessage = onSocketMessage();
            socket.onerror = onSocketError();
        } else {
            alert("The browser does not support websocket.");
        }
    
    };
    
    // Socket message
    function onSocketMessage(message) {
        console.log('Message: ' + message.data);
    };
    
    // Socket error
    function onSocketError(error) {
        console.log('Error: ' + error.data);
    };
    
    // Socket connected
    function onSocketConnected() {
        console.log("Connected to socket server");
    };
    
    // Socket disconnected
    function onSocketDisconnect() {
        console.log("Disconnected from socket server");
    };
    
    • Akosha
      Akosha about 11 years
      did u manage to find solution. I need to build something like this so will be usefull to reuse your example.
  • Display Name is missing
    Display Name is missing almost 11 years
    Please provide some sample code with your answers to better help out.