【问题标题】:how to close a blocking socket while it is waiting to receive data?如何在等待接收数据时关闭阻塞套接字?
【发布时间】:2011-09-27 16:42:16
【问题描述】:

我有一个使用阻塞套接字接收数据的服务。我遇到的问题是,如果套接字仍在等待数据,我不知道如何正确关闭它。下面是我如何打开和等待数据的简短片段:我不想实现超时,因为根据 python 文档,套接字必须阻塞才能使用makefile

我可能完全错了,因为我是使用套接字编程的新手。

编辑:

需要注意的是,我不能改变服务器的运行方式。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
reader = s.makefile("rb")
line = reader.readline()

【问题讨论】:

  • C 中的典型做法是向等待套接字的线程发送信号。信号处理程序可以longjmp out 或简单地导致readEINTR 失败。

标签: python sockets networking


【解决方案1】:

有一个非常简单的解决方案。给定一个套接字和一个像rfile = socket.makefile('rb')这样的读取器文件对象,为了使rfile.readline()在套接字关闭时立即返回,您需要在单独的线程中执行以下操作:

socket.shutdown(socket.SHUT_RDWR)
socket.close()

调用socket.shutdown() 将使rfile.readline() 返回一个空的strbytes,具体取决于Python 的版本(分别为2.x 或3.x)。

【讨论】:

  • 对我来说效果很好。谢谢!
【解决方案2】:

关闭此套接字的一种解决方案是要求服务器关闭它。

如果服务器关闭客户端套接字,客户端将收到“Connection reset by peer”错误,可能会中断阻塞接收。

另一种解决方案是不使用 readline() 并在您的套接字上设置超时(无限期等待可以等待......无限期)

【讨论】:

  • 我无权访问服务器...但是在摸索了一段时间后,我想到了编写代理服务器的可能性...虽然这可能过于复杂了..
  • 你可能是对的。我花了很多时间试图让 readline() 工作,而不是自己寻找换行符......
  • 客户端实际上会从 readLine() 收到一个空值,而不是异常。而且没有理由不能将 readLine() 与套接字超时结合使用。
  • @EJP :根据关于socket.makefile() 的python 文档,您不能将它与有超时的套接字一起使用
  • 嗯。 python 文档还说 fgets(3) 会丢弃 NUL 字节,这让我想知道 Python 文档的准确性如何,如果将带有超时的套接字传递给 socket.makefile() 会发生什么?
【解决方案3】:

我相信,如果您继续从另一个线程关闭它,那么试图从中读取的线程将返回错误。如果您没有多个线程,则必须重构以使用非阻塞模式(打开套接字时的选项os.O_NONBLOCK)。

【讨论】:

  • EBADF 是你会得到的,至少在我的系统上是这样。但这很危险,除非您绝对确定在您关闭套接字后没有线程会尝试从套接字开始读取 - 您必须使用互斥锁 - 因为否则您最终会遇到竞争条件反对打开新的套接字并获得相同的 fd。
【解决方案4】:

正如您从各种其他有些混乱、通常脆弱或危险的答案中看到的那样,这是一个棘手的问题。幸运的是,您可以通过使用非阻塞套接字来避开所有这些问题。考虑这种方法(为简单起见使用 Twisted):

from twisted.internet.protocol import ClientFactory
from twisted.protocols.basic import LineOnlyReceiver
from twisted.protocols.policies import TimeoutMixin
from twisted.internet import reactor

class YourClientProtocol(LineOnlyReceiver, TimeoutMixin):
    def connectionMade(self):
        # Initiate the timeout
        self.setTimeout(30)

    def lineReceived(self, line):
        # Reset the countdown
        self.resetTimeout()
        # And process the line somehow
        if line == "great justice":
            print "for the win!"

    def timeoutConnection(self):
        # Report the timeout
        print "Timed out waiting for a line"
        # Drop the connection
        self.transport.loseConnection()

    def connectionLost(self, reason):
        # Let the program complete
        reactor.stop()

# Set up a connection
clientFactory = ClientFactory()
clientFactory.protocol = YourClientProtocol
reactor.connectTCP(HOST, PORT)

# Start the main loop, which can handle timed and 
# network events simultaneously
reactor.run()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-01
    • 2019-07-10
    • 2015-07-30
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多