1

To understand the behaviour of the websocket, I created a simple SocketServer in java to exchange the messages. The server is expected to follow the operations as:

1) Listening at port 8080

2) A websocket handshake message generated manually on the browser client and received by the server.

3) Construct a response to the handshake message and reply to the client

4) Read out actually websocket info bytes with the same connection.

The problem has happened at step 4. when the server has responded with the handshake message, the InputStreamReader can no longer receive any new message. It blocked at the readline() method even though the client has sent the message already. From wireshark, I can see the client sent message and server respond ack. Any help would be appreciated, thanks.

Update: I just noted this question has been asked before. I will study the suggestions on the other posts first.

Update: The behavior is as same as this post: Weird websocket behavior: only send data when close the tab When the webtab closes, the read stream received the data package.

wireshark screen captures:

The tcp stream trace

The packages sequence

The log:

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: Yin4xn04vr9iBH1b2dU15A==
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: +Y9whLTzCdyN1INpAxjkO6yD2Nw=

The server socket code:

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") //<--- this line fixed the problem
    //log.debug(sb.toString());
    return sb.toString();
    }
}

The client 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>
Erli Suo
  • 11
  • 2
  • 1
    If you're attempting to implement HTTP you need a good knowledge of RFC 2616 and successors. Much more than is manifested here. Too broad. – user207421 Jul 20 '17 at 09:18
  • 1
    As I understand, regardless the protocol details, if any kind of data arrived on the listening port, I expected it could be read out. Even though it may not make any sense and further analysis may be needed, the data bytes should be available with the arriving packet. – Erli Suo Jul 20 '17 at 09:36
  • That is correct. It can be read out. One of your errors is to continue if `readLine()` returns null. That means you will loop forever at end of stream. – user207421 Jul 20 '17 at 10:32
  • Before I actually have the while loop like "while((line=readLine())!=null){...}" as most socket tutorial suggested. I changed it to current implementation only to debug to make sure whether the inputstream reads out "null" info or it is been put into "wait" state for the new message. Since the debug log "input is null" did not get printed at all. The reader must be waiting at some kinds of stream bytes queue (not sure how the stream is implemented) – Erli Suo Jul 20 '17 at 17:39
  • You aren't writing the response correctly. The line terminator in HTTP is `\r\n`, not whatever `println()` gives you, and you aren't writing a blank line after its headers. – user207421 Jul 21 '17 at 00:28
  • @EJP Yup.. you are so right. The blank line appended at the end of http header did the work! Thanks a lot! – Erli Suo Jul 21 '17 at 00:38

1 Answers1

0

Thanks a lot to EJP, the problem is a missing blank line at the response handshake to indicate the end of the message.

Erli Suo
  • 11
  • 2