socket关闭的问题

以前发在公司内网的文章,发出来共享,隐去具体项目、人员的名字。

某天检查某位同事遇到的tcp一端关闭另一端读数据报connect reset异常,问题现象是:windows部署客户端,linux上部署服务端的情况下执行操作客户端经常会报connection reset错误。

一、问题定位
第一步抓包分析,发现RST包。

第二步查tcp协议规范和java实现:
tcp常见的关闭方式通常是四次握手(很少的情况下是三次),发送两个FIN segment,两个ACK segment,参见RFC 793《TRANSMISSION CONTROL PROTOCOL》

TCP A TCP B

1. ESTABLISHED ESTABLISHED

2. (Close)
FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> --> CLOSE-WAIT

3. FIN-WAIT-2 <-- <SEQ=300><ACK=101><CTL=ACK> <-- CLOSE-WAIT

4. (Close)
TIME-WAIT <-- <SEQ=300><ACK=101><CTL=FIN,ACK> <-- LAST-ACK

5. TIME-WAIT --> <SEQ=101><ACK=301><CTL=ACK> --> CLOSED

6. (2 MSL)
CLOSED

Normal Close Sequence


TCP的协议里关闭动作意味着"我没有数据需要发送了",当我这端关闭时,我还可以接收数据,直到对方端也关闭。
RFC1122《Requirements for Internet Hosts -- Communication Layers》描述了"half-duplex" 关闭方式,即当我这端关闭时既不能读数据也不能写数据。
一般情况下这种关闭方式的网络交互方式同一,但当我这端还有数据没读的情况下会发送RST segment给对方,告诉对方你发过来的数据丢失了,不幸的是,对方的操作系统不能把这个和网络连接的RST区分开(测试情况要好一些,windows可以区分windows的,linux可以区分linux和windows的)
java里的Socket close方法在windows、linux平台上都使用的是"half-duplex" 关闭方式。

第三步检查代码,发现出问题的程序服务端的确存在有数据没读的情况,又使用了Socket close方法,所以某些情况下就会触发客户端报connection reset的错误。

二、修改办法:
1.将Socket close方法换成shutdownOutput,将不会使用"half-duplex" 关闭方式,或者
2.在Socket close之前,加上
while (true) {
if (dis.read() == -1) {
break;
}
}
确定对方已经关闭后再关闭(隐含把未读的数据也读了)。
个人觉得第二种改法更符合逻辑,应用层的功能不应该依赖底层协议的细节。
程序中最初是按第一种改法改的,后来就没动,倒也没想到什么实际的问题。

三、另外要注意的问题
关闭socket的InputStream,OutputStream时要谨慎,它们会调用Socket的关闭方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值