在使用Go编写时避免数据丢失的方法,当涉及到CLOSE_WAIT套接字时。

huangapple go评论71阅读模式
英文:

Avoiding dataloss in Go when writing with CLOSE_WAIT socket

问题

以下是翻译的内容:

  1. 使用netcat -l开始监听客户端。
  2. Go程序通过net.DialTCP与该客户端建立连接。
  3. 终止netcat。
  4. 在Go程序中,使用[]byte进行conn.Write(),它可以正常运行而没有错误!
  5. 进行另一个conn.Write时会出现错误:broken pipe(管道破裂)。

第一次写入是发生数据丢失的地方,我想要避免这种情况。如果只是得到一个错误,我知道可以保留数据并稍后重试。
我看到了类似情况的解释,链接为https://stackoverflow.com/a/15071574/2757887,这个解释似乎也适用于这里,但它仍然没有解释如何处理这个问题,如果我需要实现的TCP协议只进行单向通信。

我使用Wireshark嗅探了流量,当我终止netcat时,我可以看到它向Go程序发送FIN,然后Go程序回复ACK。由于某种原因,Go程序不立即回复自己的FIN,我很好奇为什么会这样,这可能有助于解决我的问题,但可能有一个很好的原因。

无论如何,根据http://en.wikipedia.org/wiki/Transmission_Control_Protocol中的“连接终止”部分,我得出结论,此时套接字处于CLOSE_WAIT状态,我还通过“netstat -np”确认了这一点,在终止netstat后,套接字从ESTABLISHED状态变为CLOSE_WAIT状态。

查看Wireshark,第一个conn.write会生成一个设置了push和ack字段的数据包,当然还有有效载荷。这是在Go中成功写入的部分。

然后,原来属于netstat的旧套接字发送RST,这确保在我尝试在Go中写入(第二次写入)时失败。

所以我的问题是:

A)为什么第一次写入时我无法得到错误?如果套接字收到FIN并处于CLOSE_WAIT状态,为什么Go允许我向套接字写入并告诉我一切正常?
B)在Go中是否有任何方法可以检查套接字是否处于CLOSE_WAIT状态?如果是这样,我可以将其视为关闭并不进行写入。

谢谢,
Dieter

英文:
  1. start listening client with netcat -l
  2. go program opens a conn with net.DialTCP to said client.
  3. kill the netcat
  4. in go program, do conn.Write() with a []byte -> it runs fine without error!
  5. it takes another conn.Write to get the error: broken pipe

The first write is the one where data loss happens, and I want to avoid. if i only get an error I know i can just keep the data and try again later.
I've seen https://stackoverflow.com/a/15071574/2757887 which is a very similar case and the explanation seems to apply here, but it still doesn't explain how to deal with the issue, if the tcp protocol I need to implement only does one-way communication.

I've sniffed the traffic with wireshark, and when i kill the netcat, I can see that it sends FIN to the go program, to which the go program replies with ACK. For some reason the go program doesn't immediately reply with it's own FIN - and i'm curious why that is, it might help with my problem - but there's probably a good reason for it.

Either way, from the "connection termination" section @ http://en.wikipedia.org/wiki/Transmission_Control_Protocol, I conclude that the socket is in the CLOSE_WAIT state at this point, which I also confirmed with "netstat -np", which shows the socket going from ESTABLISHED to CLOSE_WAIT after killing netstat.

Looking at wireshark, the first conn.write results in a packet with push and ack fields set, and of course my payload. this is the write that succeeds fine in go.

then the old socket that used to belong to netstat sends RST,
which makes sure that as soon as i try to write in go (2nd write) it fails.

So my question is:

A) why can't I get an error on the first write? if the socket received the FIN and is in CLOSE_WAIT why does Go let me write to the socket and tell me all is fine?
B) is there any way I can check in Go whether the socket is in CLOSE_WAIT? and if so, I could for this purpose consider it closed and not do the write.

thanks,
Dieter

答案1

得分: 3

基本上,成功的write只是告诉你数据已经被排队等待发送到另一端。如果你需要确保另一端接收到这些数据,即使连接关闭或出现错误,你必须保存数据的副本,直到另一端向你提供应用层的确认。

英文:

Fundamentally, a successful write only tells you that data has been queued to be sent to the other end. If you need to make sure the other end gets that data, even if the connection closes or errors, you must store a copy of the data until the other end provides you with an application-level acknowledgment.

huangapple
  • 本文由 发表于 2014年10月1日 04:58:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/26130010.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定