为什么我的服务器永远不会收到WebSocket发送?

时间:2012-08-24 02:36:57

标签: java javascript websocket

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

使用Chrome的开发者工具,我能够看到所有标头都已正确接收且不会引发任何错误。它还说它发送了“hello”,尽管readLine()从未在Java中发射过。

我的代码出了什么问题?

编辑1:我发现如果我刷新网页,那么(并且只有这样)ServerSocket会从最后一个连接接收数据(刷新刚刚被杀死)!为什么这是收到它的唯一方式?

编辑2:我还发现我可以在握手后向客户端发送消息,客户端会收到消息但是STILL服务器永远不会收到客户端的消息!我将消息发送给客户端,如下所示:

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

HTML PAGE

<!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 CODE

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

2 个答案:

答案 0 :(得分: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 \ n \ 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 \ n \ r \ n

解码标题

答案 1 :(得分:1)

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

对于从客户端(浏览器)到服务器的文本消息,消息的格式为:

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

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