3

我正在尝试使用基于 Javascript 的客户端和基于 Java 的服务器来实现 WebSockets。我想我已经完成了所有正确的步骤,但由于未知原因,我无法与两者建立联系。

当服务端socket接收到一个连接时,它会处理形成一个websocket-accept响应,并返回给客户端,但是客户端socket中的连接立即关闭,奇怪的是没有握手问题。

有谁知道可能是什么问题?

这是我用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);
    }
}

下面是服务器的子类 ConnectionManager:

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);
    }
}

和 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();
    }
}

这是我用 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");
};
4

2 回答 2

4

我认为,这是因为您使用的是 Java 服务器端的 Socket 包和客户端的 WebSocket API。您的想法确实很好,但技术错误。将 WebSocket 保留在客户端(Javascript),因为您没有很多其他可能性,但请尝试在服务器端(Java)使用 JWebSocket。事实上,WebSocket 使用 TCP/IP,但它自己的 TCP/IP 通信协议。Java Socket 包是纯粹的 TCP/IP。使用 JWebSocket 重新编写您的服务器,有关 JWebSocket 的所有详细信息都可以在以下位置找到:http: //jwebsocket.org/。希望我的回答对你有所帮助。

于 2013-06-04T21:20:03.887 回答
1

您必须使用“\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";

并为创建我使用的接受密钥

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);
    }
}

我建议使用http://websocket.org/echo.html来检查服务器的 websocket 功能

于 2019-02-22T05:48:08.753 回答