websocket相关问题

自己一些总结

为什么引入WebSocket协议

  http协议是一种单向的网络协议,想要实时获取服务端数据,Long Polling(http设置超时时间,服务端有数据,立即返回,收到http返回或超时都立即再发起一个请求)还是有缺陷,1、server更新速度快,server在传送一个数据包给client后必须等待下一个Get请求到来,才能传递第二个更新的数据包给Browser,那么这样的话,Browser显示实时数据最快的时间为2×RTT(往返时间),另外在网络拥塞的情况下,这个应该是不能让用户接受的;2、由于http数据包的头部数据量往往很大(通常有400多个字节),但是真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费。
  此时,websocket就华丽出场啦!
转载自:http://blog.csdn.net/yl02520/article/

遇到问题

BINARY_PARTIAL_WRITING

  此问题的原因是:多线程在同时发送消息。需要加锁。
以下是错误代码:

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
Thread.sleep(1 * 1000);
UserProtos.User user = UserProtos.User.parseFrom(message.getPayload());
System.out.println(user.getName());
try {
session.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
}

强制关闭现有连接

  对此进行改进以后,还是报错:java.io.IOException: java.io.IOException: 远程主机强迫关闭了一个现有的连接。发送之前判断session是否打开。
  session突然关闭,handler的handleTextMessage方法还是会接收到消息的。所有数据处理之前,可以使用session.isOpen()来判断session的状态。

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
UserProtos.User user = UserProtos.User.parseFrom(message.getPayload());
System.out.println(user.getName());
try {
if (session.isOpen()) {
session.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}

timeoutException

  同时发现会报错:WritePendingException
  这种问题在服务端处理的比较慢的时候出现。
  为了更改的找出问题所在,客户端一秒发送100条数据,服务端一秒钟处理一条数据(线程休眠),出现此问题。
  因为:很多数据存储在缓存中,无法继续写入,超时。
  至于写绑定异常,本人认为是:超时关闭,写入关闭原因,而之前的数据超时没写完,两个一起写造成的。

websocket超时断开

  两个socket间,如果不长时间进行通信,会自动断开。虽然断开可以接收到通知,但是还有路由等原因会导致断开,是接不到通知的,察觉不到。一端认为还在,另一端已经断开了。
解决办法:
  发送心跳包

IOException:Too many open files

服务器无法联网,websocket连接其他服务器,报错UnresolvedAddressException:

1
2
3
4
5
6
7
8
9
java.nio.channels.UnresolvedAddressException: null
at sun.nio.ch.Net.checkAddress(Net.java:101) ~[na:1.8.0_101]
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.implConnect(UnixAsynchronousSocketChannelImpl.java:301) ~[na:1.8.0_101]
at sun.nio.ch.AsynchronousSocketChannelImpl.connect(AsynchronousSocketChannelImpl.java:199) ~[na:1.8.0_101]
at org.apache.tomcat.websocket.WsWebSocketContainer.connectToServer(WsWebSocketContainer.java:297) ~[tomcat-embed-websocket-8.5.16.jar!/:8.5.16]
at org.springframework.web.socket.client.standard.StandardWebSocketClient$1.call(StandardWebSocketClient.java:150) ~[spring-websocket-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.web.socket.client.standard.StandardWebSocketClient$1.call(StandardWebSocketClient.java:147) ~[spring-websocket-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_101]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_101]

没有联网,报错正常,但是运行一段时间后,报了以下错误。

1
2
3
4
5
6
7
2017-11-21 05:30:00,010:ERROR http-nio-8888-Acceptor-0 (DirectJDKLog.java:181) - Socket accept failed
java.io.IOException: Too many open files
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) ~[?:1.8.0_101]
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422) ~[?:1.8.0_101]
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250) ~[?:1.8.0_101]
at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:457) [tomcat-embed-core-8.5.4.jar!/:8.5.4]
at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]

查看进程号:ps -ef | grep java 再用命令lsof -p 进程号 | wc -l发现打开的文件数量很多。lsof -p 进程号则发现有大量类似的:can’t identify protocol

1
java 13597 root 51u sock 0,6 0t0 185981 can't identify protocol

What?什么原因?网络搜索查看原因,解释说socket没有关闭。定位到打开socket的地方,只是简单的下面几行:

1
2
3
4
5
StandardWebSocketClient client = new StandardWebSocketClient();
WebSocketConnectionManager manager = new WebSocketConnectionManager(
client, handler, WS_URI);
manager.setOrigin(ORIGIN);
manager.start();

不明白,查看源码,追踪到WsWebSocketContainer中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public Session connectToServer(Endpoint endpoint,
ClientEndpointConfig clientEndpointConfiguration, URI path)
throws DeploymentException {
// ......
// Open the connection
Future<Void> fConnect = socketChannel.connect(sa);
AsyncChannelWrapper channel = null;
// ......
if (secure) {
SSLEngine sslEngine = createSSLEngine(userProperties);
channel = new AsyncChannelWrapperSecure(socketChannel, sslEngine);
} else if (channel == null) {
channel = new AsyncChannelWrapperNonSecure(socketChannel);
}
try {
// ......
} catch (ExecutionException | InterruptedException | SSLException |
EOFException | TimeoutException | URISyntaxException e) {
throw new DeploymentException(
sm.getString("wsWebSocketContainer.httpRequestFailed"), e);
} finally {
if (!success) {
channel.close();
}
}
// ......
}

channel.close()实则关闭的就是socketChannel,所以,如果直接写ip,没事,写域名连接,每次报错在connect(sa)处报错,没有关闭。
解决:
  在调用创建socket连接之前,判断了下是否可以连接。

参考

http://www.cnblogs.com/highriver/archive/2012/01/16/2324035.html
https://my.oschina.net/ldl123292/blog/304360
Spring Websocket Session共享解决思路(背景)http://www.sojson.com/blog/238.html
看完让你彻底搞懂Websocket原理 http://blog.csdn.net/frank_good/article/details/50856585
webSocket中并发的策略 http://blog.csdn.net/heshuangyuan123/article/details/39023569
tcp socket文件句柄泄漏
http://mdba.cn/2015/03/10/tcp-socket文件句柄泄漏/

文章目录
  1. 1. 为什么引入WebSocket协议
  2. 2. 遇到问题
    1. 2.1. BINARY_PARTIAL_WRITING
    2. 2.2. 强制关闭现有连接
  3. 3. timeoutException
  4. 4. websocket超时断开
  5. 5. IOException:Too many open files
  6. 6. 参考
|