确定对等体是否已关闭套接字的读取结束

R..*_*R.. 13 c sockets posix

我有一个套接字编程情况,客户端关闭套接字的写入端,让服务器知道输入已完成(通过接收EOF),但保持读取结束打开以读回结果(一行文本).服务器知道客户端已成功读取结果并关闭套接字(或至少关闭读取端)将是有用的.有没有一种检查/等待这种状态的好方法?

use*_*421 5

不可以.所有你能知道的是你的发送是否成功,并且由于TCP缓冲,其中一些甚至在对等读取关闭后也会成功.

这是糟糕的设计.如果服务器需要知道客户端收到数据,则客户端需要确认它,这意味着它无法关闭其写入端.客户应该:

  1. 发送带内终止消息,作为数据.
  2. 读取并确认所有进一步的响应,直到流结束.
  3. 关闭插座.

服务器应检测带内终止消息,并且:

  1. 停止从套接字读取请求
  2. 发送所有未完成的回复并阅读确认
  3. 关闭插座.

或者,如果目标只是确保客户端和服务器同时结束,则每个端都应关闭其套接字以进行输出,然后读取输入直到流结束,然后关闭套接字.这样,最终关闭将在两端或多或少同时发生.


Dim*_*nek 5

getsockoptTCP_INFO似乎是最明显的选择,但它不是跨平台的.

这是Linux的一个例子:

import socket
import time
import struct
import pprint


def tcp_info(s):
    rv = dict(zip("""
            state ca_state retransmits probes backoff options snd_rcv_wscale
            rto ato snd_mss rcv_mss unacked sacked lost retrans fackets
            last_data_sent last_ack_sent last_data_recv last_ack_recv
            pmtu rcv_ssthresh rtt rttvar snd_ssthresh snd_cwnd advmss reordering
            rcv_rtt rcv_space
            total_retrans
            pacing_rate max_pacing_rate bytes_acked bytes_received segs_out segs_in
            notsent_bytes min_rtt data_segs_in data_segs_out""".split(),
                  struct.unpack("BBBBBBBIIIIIIIIIIIIIIIIIIIIIIIILLLLIIIIII",
                                s.getsockopt(socket.IPPROTO_TCP, socket.TCP_INFO, 160))))
    wscale = rv.pop("snd_rcv_wscale")

    # bit field layout is up to compiler
    # FIXME test the order of nibbles
    rv["snd_wscale"] = wscale >> 4
    rv["rcv_wscale"] = wscale & 0xf
    return rv

for i in range(100):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("localhost", 7878))
    s.recv(10)
    pprint.pprint(tcp_info(s))
Run Code Online (Sandbox Code Playgroud)

我怀疑是否存在真正的跨平台替代方案.

从根本上说,有很多州:

  • 你把数据写入套接字,但它尚未发送
  • 数据已发送,但未收到
  • 数据已发送和丢失(依赖于计时器)
  • 数据已收到,但尚未确认
  • 尚未收到确认
  • 确认丢失(依赖于计时器)
  • 数据由远程主机接收,但未被应用程序读取
  • 数据由应用程序读出,但socket仍然存活
  • 数据被读出,应用程序崩溃了
  • 数据被读出,app关闭了套接字
  • 数据被读出,应用程序被调用shutdown(WR)(几乎与关闭相同)
  • FIN尚未通过远程发送
  • FIN是通过远程发送但尚未收到的
  • FIN被送去并迷路了
  • FIN收到你的结尾

显然,你的操作系统可以区分这些状态中的很多,但不是全部.我想不出一个API就是这个冗长的......

某些系统允许您查询剩余的发送缓冲区空间.也许如果你这样做,并且套接字已经关闭,你会得到一个整洁的错误?

好消息是因为套接字被关闭,并不意味着你无法查询它.TCP_INFO关闭后,我可以获得所有state=7关闭.在某些情况下报告state=8(等待等待).

http://lxr.free-electrons.com/source/net/ipv4/tcp.c#L1961拥有Linux TCP状态机的所有细节.