【问题标题】:Why does my server never receive WebSocket send?为什么我的服务器从不接收 WebSocket 发送?
【发布时间】:2012-08-24 02:36:56
【问题描述】:

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

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

我的代码有什么问题?

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

HTML 页面

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

【问题讨论】:

    标签: java javascript websocket


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 谢谢,这就是问题所在!我忘了从 http 协议切换到 web 套接字。
    • HTTP/1.1 101 Switching Protocols 尚未被 Web 客户端接受,客户端仍在等待 http 响应以 2 CRLF 完成。
    【解决方案2】:

    你的代码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 解码您的标头。

    【讨论】:

      猜你喜欢
      • 2011-11-30
      • 2011-12-15
      • 2021-10-01
      • 1970-01-01
      • 2016-07-06
      • 2014-08-20
      • 2010-10-14
      • 1970-01-01
      • 2021-07-11
      相关资源
      最近更新 更多