onlyliuxin / coding2017

218 stars 643 forks source link

socket inputstream读取不到websocket 数据包,请大家帮忙看看。 #520

Closed earlywusa closed 7 years ago

earlywusa commented 7 years ago

尝试用java socket实现简单的websocket信息交换,我的理解是分成以下的简单几步: 1)建立ServerSocket,并且监听 8080 (自己制定的端口) 2)用javascript建立Websocket,并发送 handshake 信息。 3)ServerSocket 收到 握手请求,发送回复 4)Websocket收到握手回复,验证sec-websocket-accept header,如果正确,发送消息数据包 5)ServerSocket 收到数据包,进行websocket数据包格式验证,读取payload. 我的问题是出在第5步, 验证的sec-websocket-accept显示正确,但回复的数据包在inputstream上面读不出来。 感觉就是readline()命令完全没有收到任何byte,哪怕是encoded的数据, 一直wait在数据流上面。 从wireshark上来看,client端已经发送了数据,server也返回了ack回复, 说明收到了,可是stream就是没有消息。。

以下是全部log, readline在发了握手回复之后再也没有收到inputstream的消息。 inputline: GET / HTTP/1.1 inputline: Host: localhost:8080 inputline: Connection: Upgrade inputline: Pragma: no-cache inputline: Cache-Control: no-cache inputline: Upgrade: websocket inputline: Origin: file:// inputline: Sec-WebSocket-Version: 13 inputline: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 inputline: Accept-Encoding: gzip, deflate, br inputline: Accept-Language: en,zh-TW;q=0.8,zh;q=0.6,zh-CN;q=0.4 inputline: Sec-WebSocket-Key: keY/5XS7qWTLVop5h88axw== inputline: Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits inputline: response: HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: /f4sn4/rmtjrgIUTRUhJPkS6KoQ=

这里是chrome的截图,显示收到了握手回复 screen shot 2017-07-20 at 11 36 32 am 这里是wireshark的tcp stream 截图和package sequence的截图 screen shot 2017-07-20 at 2 13 08 am screen shot 2017-07-20 at 2 13 33 am

以下是我的test server 代码。 ` package com.early.main;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Date;
    import java.util.concurrent.TimeUnit;

    import org.apache.commons.codec.binary.Base64;
    import org.apache.commons.codec.digest.DigestUtils;

    import com.early.socket.message.HttpMessage;
    import com.early.socket.message.Message;

    public class EchoServer {
        public static String HTTP_VERSION_HEADER = "HTTP/1.1";
        public static void main(String[] args) throws IOException {

            int portNumber = 8080;

            try (
                ServerSocket serverSocket =
                    new ServerSocket(Integer.parseInt("8080"));
                Socket clientSocket = serverSocket.accept();     
                PrintWriter out =
                    new PrintWriter(clientSocket.getOutputStream(), true);                   
                BufferedReader in = new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream()));
            ) {
                String inputLine;
                StringBuilder sb = new StringBuilder();
                while ( true) {
                        inputLine = in.readLine();
                        if(inputLine == null) {
                            System.out.println("input is null");
                            continue;
                        }
                        System.out.println("inputline: " + inputLine);
                        sb.append(inputLine).append(System.lineSeparator());
                        if(inputLine.equals("")) {
                                Message msg = new Message(sb.toString(), new Date());
                                HttpMessage tmpMessage = new HttpMessage(msg.getText(), new Date());
                                String response = generateHandshakeResponse(tmpMessage);
                                System.out.println("response: " + response);
                                out.println(response);
                                out.flush();
                        }
                }
            } catch (IOException e) {
                System.out.println("Exception caught when trying to listen on port "
                    + portNumber + " or listening for a connection");
                System.out.println(e.getMessage());
            }
        }

        private static String generateHandshakeResponse(HttpMessage message) {
            String webSocketKey = message.getVal(HttpMessage.SEC_WEBSOCKET_KEY);
            String webSocketAccept = webSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            byte[] bytes = DigestUtils.sha1(webSocketAccept);
            String secWebSocketAcceptVal = Base64.encodeBase64String(bytes);

            StringBuilder sb = new StringBuilder();
            sb.append(HTTP_VERSION_HEADER).append(" 101 ").append("Switching Protocols\r\n");
            sb.append("Upgrade: websocket\r\n");
            sb.append("Connection: Upgrade\r\n");
            sb.append("Sec-WebSocket-Accept: ").append(secWebSocketAcceptVal).append("\r\n");
                   sb.append("/n/n"); //<--- 这行修复了问题
            //log.debug(sb.toString());
            return sb.toString();
        }
    }

`

以下是我javascript websocket 的代码 (github的code格式怎么也没有调整好, 请大家凑合看一下):

<!doctype html>
<html lang="en">
<head>
  <title>Websocket Client</title>
</head>
<body>
  <script>
    var exampleSocket = new WebSocket("ws://localhost:8080");
    exampleSocket.onopen = function (event) {
      console.log("connection opened..");
      exampleSocket.send("Can you hear me?"); 
    };
    exampleSocket.onmessage = function (event) {
      console.log(event.data);
    }

    function sendMsg(){
      console.log("send message..");
      exampleSocket.send("hello hello");
    }
  </script>
  <button onclick="sendMsg()" title="send">send</button>
</body>
</html>

earlywusa commented 7 years ago

类似的情况在stackoverflow上也有一个帖子。https://stackoverflow.com/q/10742247/7466185 在网页关闭的时候, inputstream可以读出来数据了。 这个好奇怪。 感觉就像是websocket 没有flush一样。 有没有谁对前端websocket比较熟的可以给出一些建议

earlywusa commented 7 years ago

问题找到了。。少打了一个空行去标识信息结束。。