onlyliuxin/coding2017

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

Closed this issue · 2 comments

尝试用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>

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

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