为啥 InputStreamReader 无法读取 websocket 包的内容?
Posted
技术标签:
【中文标题】为啥 InputStreamReader 无法读取 websocket 包的内容?【英文标题】:Why InputStreamReader cannot read the content of a websocket package?为什么 InputStreamReader 无法读取 websocket 包的内容? 【发布时间】:2017-07-20 09:10:00 【问题描述】:为了了解 websocket 的行为,我在 java 中创建了一个简单的 SocketServer 来交换消息。服务器应遵循以下操作:
1) 监听 8080 端口
2) 在浏览器客户端手动生成并由服务器接收的 websocket 握手消息。
3) 构造对握手消息的响应并回复客户端
4) 实际读取同一个连接的 websocket 信息字节。
问题出现在第4步。当服务器响应握手消息时,InputStreamReader不能再收到任何新消息。即使客户端已经发送了消息,它也会在 readline() 方法处阻塞。从wireshark,我可以看到客户端发送消息和服务器响应确认。任何帮助将不胜感激,谢谢。
更新:我刚刚注意到以前有人问过这个问题。我会先研究其他帖子的建议。
更新:行为与这篇文章相同: Weird websocket behavior: only send data when close the tab 当 webtab 关闭时,读取流接收到数据包。
wireshark 屏幕截图:
The tcp stream trace
The packages sequence
日志:
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=
服务器套接字代码:
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();
客户端代码:
<!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>
【问题讨论】:
如果您尝试实现 HTTP,您需要对 RFC 2616 和后续版本有很好的了解。远不止于此。太宽泛了。 据我了解,无论协议细节如何,如果有任何类型的数据到达侦听端口,我希望它可以被读出。尽管它可能没有任何意义并且可能需要进一步分析,但数据字节应该与到达的数据包一起可用。 没错。可以读出。如果readLine()
返回null,您的错误之一是继续。这意味着您将在流结束时永远循环。
在我实际上有像大多数套接字教程建议的“while((line=readLine())!=null)...”这样的while循环之前。我将其更改为当前实现只是为了进行调试以确保输入流是否读出“空”信息或者它是否被置于新消息的“等待”状态。由于调试日志“输入为空”根本没有打印出来。阅读器必须在某种流字节队列中等待(不确定流是如何实现的)
您没有正确编写响应。 HTTP 中的行终止符是\r\n
,而不是println()
给你的任何东西,而且你没有在它的标题后面写一个空行。
【参考方案1】:
非常感谢 EJP,问题是响应握手时缺少空行以指示消息结束。
【讨论】:
以上是关于为啥 InputStreamReader 无法读取 websocket 包的内容?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 InputStreamReader 从 jar 读取时会抛出 NPE?
读取所有字符时是不是有理由使用 BufferedReader 而不是 InputStreamReader?
Java读取文件-BufferedReader/FileReader/InputStreamReader/FileInputStream的关系和区别
如何使用 Java 的 BufferedReader 与 InputStreamReader 读取文件?
Java笔记-解决读取文件时中文乱码问题(InputStreamReader设置编码)
Java 之 FileReader FileInputStream InputStreamReader BufferedReader 作用与区