Netty服务端请求未响应链接关闭-客户端半关闭问题
问题表现:
有一个服务是使用Netty 做 Socket 服务端,对接的第三方客户端调用时,服务端能收到请求,但是客户端未收到响应。
排查过程:
自己写客户端测试正常,使用JDK Socket客户端和Netty客户端均正常,并且服务端代码有很多业务方已经接入,线上运行一年多,服务正常,暂时先排除服务端代码问题。
怀疑与客户端代码有关系,让客户将客户端代码发过来验证
1
2
3
4
5
6
7
8//....省略业务代码
Socket socket = new Socket(ip, port);
socket.setSoTimeout(30000);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(send.getBytes("GBK"));
out.flush();
socket.shutdownOutput();//问题关键代码
//....省略业务代码经过对比,这个客户的客户端多了
socket.shutdownOutput()
,去掉之后就正常。我们来先看下这个方法的源码注释:
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/**
* 将此套接字的输入流置于“流的结尾”。
* 确认发送到套接字输入流端的任何数据
* 然后默默丢弃。
* <p>
* 如果在调用此方法后从套接字输入流中读取
* 套接字,流的 {@code available} 方法将返回 0,它的
* {@code read} 方法将返回 {@code -1}(流结束)。
*
* @exception IOException if an I/O error occurs when shutting down this
* socket.
*
* @since 1.3
* @see java.net.Socket#shutdownOutput()
* @see java.net.Socket#close()
* @see java.net.Socket#setSoLinger(boolean, int)
* @see #isInputShutdown
*/
public void shutdownInput() throws IOException
{
if (isClosed())
throw new SocketException("Socket is closed");
if (!isConnected())
throw new SocketException("Socket is not connected");
if (isInputShutdown())
throw new SocketException("Socket input is already shutdown");
getImpl().shutdownInput();
shutIn = true;
}我的理解是socket.shutdownOutput()方法,它是一种单向关闭流的方法,即关闭客户端的输出流并不会关闭服务端的输出流。通过shutdownOutput()方法只是关闭了输出流,但socket仍然是连接状态,连接并未关闭。正常来说是不应该影响正常消息接收的。
那么为什么会收不到响应呢?接下来继续验证
将Netty服务端替换为JDK net包的SocketServer实现后,此时接收发送消息均正常了
那么问题基本可以定位到是Netty的问题了。查询资料
TCP和SCTP允许用户关闭一个socket的出站流量而不用完全关闭它。这样的socket被称为“半关闭socket”,同时用户能够通过调用SocketChannel.shutdownOutput()方法来获取一个半关闭socket。如果一个远端关闭了出站通道,SocketChannel.read(..)会返回-1,这看上去并没有和一个关闭了的链接有什么区别。
3.x没有shutdownOutput()操作。同样,它总是在SocketChannel.read(..)返回-1的时候关闭链接。
要支持半关闭socket,4.0增加了SocketChannel.shutdownOutput()方法,同时用户能设置“ALLOW_HALF_CLOSURE”的ChanneOption来阻止Netty在SocketChannel.read(..)返回-1的时候自动关闭链接
解决方案:
尝试设置ChannelOption参数ALLOW_HALF_CLOSURE=true ,允许半关闭socket即可。默认为false。