2

我正在实现一个 WebSocket 服务器(用于学习目的),并且我让它正确处理握手(websocket.onopen 被调用,所以我认为这意味着握手成功),但是,当客户端(浏览器)在握手后发送消息时,服务器永远不会收到它。

使用 Chrome 的开发人员工具,我可以看到所有标头都已正确接收,并且没有引发任何错误。它还说尽管 readLine() 从未在 Java 中触发,但它发送了“你好”。

我的代码有什么问题?

编辑1:我发现如果我刷新网页,那么(并且只有那时)ServerSocket 接收来自最后一个连接的数据(刷新刚刚终止)!为什么这是它接收它的唯一方式?

编辑2:我还发现我可以在握手后向客户端发送消息并且客户端收到它但仍然服务器从未收到客户端的消息!我像这样将消息发送给客户端:

byte[] message = new byte[ 7 ];
message[ 0 ] = new Integer(129).byteValue();
message[ 1 ] = new Integer(5).byteValue();
byte[] raw = "hello".getBytes();
message[ 2 ] = raw[ 0 ];
message[ 3 ] = raw[ 1 ];
message[ 4 ] = raw[ 2 ];
message[ 5 ] = raw[ 3 ];
message[ 6 ] = raw[ 4 ];
outStream.write( message);
out.println();

网页

<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <title>WebSocket Test</title></head>

    <body>
        <script>
            try
            {
                function writeToScreen(message)
                {
                    var p = document.createElement( "p" );
                    p.innerHTML = message;
                    document.getElementsByTagName( "body" )[ 0 ].appendChild( p );
                }

                function onOpen(evt)
                {
                    writeToScreen( "opened" );
                    doSend( "hello" );
                    //We reach here but the server never recieves the message! (and bufferedAmount == 0)
                    writeToScreen( "sent: " + websocket.bufferedAmount );
                }
                function onClose(evt)
                {
                    alert( "closed" );
                    websocket.close();
                }
                function onMessage(evt)
                {
                    alert( "Message: " + evt.data );
                }
                function onError(evt)
                {
                    alert( "Error: " + evt );
                }

                function doSend (message)
                {   
                    websocket.send( message );
                }

                //PUT IN YOUR OWN LOCAL IP ADDRESS HERE TO GET IT TO WORK
                var websocket = new WebSocket( "ws://192.168.1.19:4444/" );
                websocket.onopen = onOpen;
                websocket.onclose = onClose;
                websocket.onmessage = onMessage;
                websocket.onerror = onError;

            }
            catch(e)
            {
            }
        </script>
    </body>
</html>

JAVA代码

import java.net.*;
import java.io.*;
import java.security.*;

public class WebListener
{
    public static void main(String[] args) throws Exception
    {
        ServerSocket serverSocket = null;
        boolean listening = true;

        try {
            serverSocket = new ServerSocket(4444);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 4444.");
            System.exit(-1);
        }

        while (listening) new ServerThread(serverSocket.accept()).start();

        serverSocket.close();
    }
}

class ServerThread extends Thread {
    private Socket socket = null;

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

    public void run() {

        try {
            OutputStream outStream = null;
            PrintWriter out = new PrintWriter( outStream = socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream()));

            String inputLine, outputLine;

            //Handle the headers first
            doHeaders( out, in );

            //Now read anything they have to send
            while ( ( inputLine = in.readLine() ) != null )
            {
                //WE NEVER REACH HERE!
                System.out.println( inputLine );
            }

            out.close();
            in.close();
            socket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void doHeaders(PrintWriter out, BufferedReader in) throws Exception
    {
        String inputLine = null;
        String key = null;

        //Read the headers
        while ( ( inputLine = in.readLine() ) != null )
        {
            //Get the key
            if ( inputLine.startsWith( "Sec-WebSocket-Key" ) ) key = inputLine.substring( "Sec-WebSocket-Key: ".length() );

            //They're done
            if ( inputLine.equals( "" ) ) break;
        }

        //We need a key to continue
        if ( key == null ) throw new Exception( "No Sec-WebSocket-Key was passed!" );

        //Send our headers
        out.println( "HTTP/1.1 101 Web Socket Protocol Handshake\r" );
        out.println( "Upgrade: websocket\r" );
        out.println( "Connection: Upgrade\r" );
        out.println( "Sec-WebSocket-Accept: " + createOK( key ) + "\r" );
        out.println( "\r" );
    }

    public String createOK(String key) throws NoSuchAlgorithmException, UnsupportedEncodingException, Exception
    {
        String uid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
        String text = key + uid;

        MessageDigest md = MessageDigest.getInstance( "SHA-1" );
        byte[] sha1hash = new byte[40];
        md.update( text.getBytes("iso-8859-1"), 0, text.length());
        sha1hash = md.digest();

        return new String( base64( sha1hash ) );
    }

    public byte[] base64(byte[] bytes) throws Exception
    {
        ByteArrayOutputStream out_bytes = new ByteArrayOutputStream();
        OutputStream out = new Base64.OutputStream(out_bytes); //Using http://iharder.net/base64
        out.write(bytes);
        out.close();
        return out_bytes.toByteArray();
    }

    private String convertToHex(byte[] data) { 
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < data.length; i++) { 
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do { 
                if ((0 <= halfbyte) && (halfbyte <= 9)) 
                    buf.append((char) ('0' + halfbyte));
                else 
                    buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        } 
        return buf.toString();
    } 
}
4

2 回答 2

1

你的代码 WebListener 必须在 windows 上运行,你会发现 line.separator 是 CRLF

    byte[] lineSeperator=System.getProperty("line.separator").getBytes();
    System.out.println("line seperator: "+Arrays.toString(lineSeperator));

在您的响应标头中

    out.println( "Header xxxx"+ "\r" );

所以标题以 \r\r\n 结尾

根据 HTTP rfc2616

   Response      = Status-Line               ; Section 6.1
                   *(( general-header        ; Section 4.5
                    | response-header        ; Section 6.2
                    | entity-header ) CRLF)  ; Section 7.1
                   CRLF
                   [ message-body ]          ; Section 7.2

客户端无法使用 \r\r\n 解码您的标头。

于 2012-08-24T05:34:17.463 回答
1

WebSocket 消息不会被终止,\r\n因此您无法使用in.readline()它们来读取它们。有关如何构造消息,请参阅规范的数据框架部分。

对于从客户端(浏览器)到服务器的文本消息,消息将具有以下形式:

  • (字节)0x81
  • 1、3 或 9 字节结构,指示消息长度以及消息正文是否被屏蔽。(来自浏览器的消息应始终被屏蔽。)
  • 4字节掩码
  • 消息(utf-8 编码)

没有您可以搜索的消息结束标记。您只需要读取客户端请求的前几个字节即可确定其有效负载的长度。

于 2012-08-24T08:19:27.317 回答