Java server JavaScript client WebSockets

2020-02-26 09:57发布

问题:

I'm trying to do a connection between a server in Java and a JavaScript client but I'm getting this error on client side:

WebSocket connection to 'ws://127.0.0.1:4444/' failed: Connection closed before receiving a handshake response

It maybe stays on OPENNING state because the connection.onopen function is never called. The console.log('Connected!') isn't being called.

Could someone let me know what is going wrong here?

Server

import java.io.IOException;
import java.net.ServerSocket;

public class Server {

    public static void main(String[] args) throws IOException {

        try (ServerSocket serverSocket = new ServerSocket(4444)) {
            GameProtocol gp = new GameProtocol();

            ServerThread player= new ServerThread(serverSocket.accept(), gp);
            player.start();

        } catch (IOException e) {
            System.out.println("Could not listen on port: 4444");
            System.exit(-1);
        }

    }

}

ServerThread

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerThread extends Thread{

    private Socket socket = null;
    private GameProtocol gp;

    public ServerThread(Socket socket, GameProtocol gp) {
        super("ServerThread");
        this.socket = socket;
        this.gp = gp;
    }

    public void run() {

        try (
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(
                                socket.getInputStream()));
                ) {
            String inputLine, outputLine;

            while ((inputLine = in.readLine()) != null) {
                outputLine = gp.processInput(inputLine);
                System.out.println(outputLine);
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

GameProtocol

public class GameProtocol {

    public String processInput(String theInput) {

        String theOutput = null;

        theOutput = theInput;

        return theOutput;
    }
}

Client

var connection = new WebSocket('ws://127.0.0.1:4444');

connection.onopen = function () {
    console.log('Connected!');
    connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
    console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
    console.log('Server: ' + e.data);
};

回答1:

To start with, both your code looks identical the Java and JavaScript one. Both work for what they are design to, but the facts is that you are trying to connect a WebSocket client to a socket server.

As I know they are two different things regarding this answer.

I have never tried it your way. That said if I have a network application that use socket than it would be pure client/server socket, and if it was a web application than I would use WebSocket on both side as well.

So far so good..

To make this work, this answer suggests to use any available WebSocket on server side and your problem is solved.

I am using WebSocket for Java and here is a sample implementation that I have tested with your client code and it works, both on client and server side.

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;

public class WebsocketServer extends WebSocketServer {

    private static int TCP_PORT = 4444;

    private Set<WebSocket> conns;

    public WebsocketServer() {
        super(new InetSocketAddress(TCP_PORT));
        conns = new HashSet<>();
    }

    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        conns.add(conn);
        System.out.println("New connection from " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
    }

    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        conns.remove(conn);
        System.out.println("Closed connection to " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
    }

    @Override
    public void onMessage(WebSocket conn, String message) {
        System.out.println("Message from client: " + message);
        for (WebSocket sock : conns) {
            sock.send(message);
        }
    }

    @Override
    public void onError(WebSocket conn, Exception ex) {
        //ex.printStackTrace();
        if (conn != null) {
            conns.remove(conn);
            // do some thing if required
        }
        System.out.println("ERROR from " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
    }
}

On your main method just:

new WebsocketServer().start();

You might need to manipulate your code to fit it with this implementation, but that should be part of the job.

Here is the test output with 2 tests:

New connection from 127.0.0.1
Message from client: Ping
Closed connection to 127.0.0.1
New connection from 127.0.0.1
Message from client: Ping

here is WebSocket maven configuration, otherwise download the JAR file/s manually and import it in your IDE/development environment:

<!-- https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket -->
<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.3.0</version>
</dependency>

Link to WebSocket.