问题问题简述如下图. server docker restart后, client端写入的日志丢失, 并且无报错.因为不支持时序图, 把时序图代码嵌入在代码里.sequenceclient->server: log_dataclient->server: log_dataserver->server: docker restartserver->client: finclient->server: log_data loss without errortcp state diagram问题定位过程为什么卡在CLOSE_WAIT.看tcp状态转换图, 可以看到client收到了fin, 一直没有recv, 一直卡在CLOSE_WAIT. 和实际的代码是吻合的. 那么, 为什么在server docker restart 引发CLOSE_WAIT后, client发消息仍然不报错呢?因为:tcp协议允许client在收到fin后, 继续发送消息.server 在docker restart后 ip 改变, client还是往原来的ip发送消息, 没有主机通知client rst, 导致消息在系统buffer里积压.积压信息如下:root@9eeaefa7fe57:/# netstat -nap | grep 27017 | grep 10.0.0tcp 1 402 10.0.0.186:62281 10.0.0.16:27017 CLOSE_WAIT 4308/serverroot@9eeaefa7fe57:/# netstat -nap | grep 27017 | grep 10.0.0tcp 1 70125 10.0.0.186:62281 10.0.0.16:27017 CLOSE_WAIT 4308/server此时, 在elixir socket接口层面来看, 不管socket的状态, 还是发送, 都是ok的.iex(client@client.)25> socket |> :inet.port{:ok, 57395}iex(client@client.)26> socket |> :gen_tcp.send(“aaa”):ok如果主动close, 则会进入LAST_ACK状态iex(client@client.)27> socket |> :gen_tcp.close() :okroot@9eeaefa7fe57:/# netstat -nap | grep 27017 | grep 10.0.0tcp 1 70126 10.0.0.186:62281 10.0.0.16:27017 LAST_ACK - CLOSE_WAIT的恢复如果代码还是只发不收. 是检测不到CLOSE_WAIT的. 显然, 应用层心跳是一个解决方案. 那么, 不使用心跳, 只发不收的情况下, 什么时候才能检测到错误呢?send buffer 满todo 深究tcp keepalive, 不使用 keepalive情况下的 tcp 最大链接空闲时间.