【问题标题】:Python socket error on UDP data receive. (10054)UDP 数据接收上的 Python 套接字错误。 (10054)
【发布时间】:2011-02-04 08:05:30
【问题描述】:

我目前在使用 UDP 和 Python 套接字模块时遇到问题。我们有服务器和客户端。当我们向用户发送数据时,就会出现问题。用户可能通过客户端崩溃、ISP 断开连接或其他一些不正确的方法关闭了与服务器的连接。因此,可以将数据发送到关闭的套接字。

当然,对于 UDP,您无法判断数据是否真的到达或是否已关闭,因为它不在乎(至少,它不会引发异常)。但是,如果您发送数据并且它被关闭,您会以某种方式获取数据(???),这最终会在 sock.recvfrom 上给您一个套接字错误。 [Errno 10054] 现有连接被远程主机强行关闭。几乎看起来像是来自连接的自动响应。

虽然这很好,并且可以通过 try: except: block 来处理(即使它会稍微降低服务器的性能)。问题是,我不知道这是来自谁或关闭了哪个套接字。无论如何要找出'谁'(ip,socket#)发送了这个?太好了,因为我可以立即断开它们并将它们从数据中删除。有什么建议?谢谢。

服务器:

import socket

class Server(object):
    def __init__(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.connected = {}

    def connect(self):
        self.socket.bind(('127.0.0.1', 5579))

    def find_data(self):
        while 1:
            data, address = self.socket.recvfrom(1024)
            self.got_data(data,address)
            if self.connected.has_key(address):
                pass
            else:
                self.connected[address] = None

    def got_data(self, data, address):
        print "GOT",data,"FROM",address
        for people in self.connected:
            print people
            self.send_data('hi', people)

    def send_data(self, data, address):
        self.socket.sendto(data,address)


if __name__ == '__main__':
    server = Server()
    server.connect()
    print "NOW SEARCHING FOR DATA"
    server.find_data()

客户:

import socket, time

class Client(object):
    def __init__(self):
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    def connect(self):
        self.socket.connect(('127.0.0.1', 5579))

    def send_data(self):
        self.socket.sendto('hi',('127.0.0.1', 5579))

    def got_data(self, data, address):
        print "GOT",data,"FROM",address


if __name__ == '__main__':
    client = Client()
    client.connect()
    while 1:
        client.send_data()
        time.sleep(5)

【问题讨论】:

  • 这似乎不是没有连接的 UDP 问题。你能展示导致问题的最小代码吗?请尝试将最小的代码块与客户端和服务器隔离开来。
  • 好的,我只使用这个示例代码就很容易地重新创建了它。我不确定我可以在这里全部粘贴,所以我将使用粘贴点。服务器:paste-it.net/public/o230dad 客户端:paste-it.net/public/k194612 基本上,只要打开服务器和两个客户端。等待它循环got_data,然后关闭一个客户端。繁荣,服务器中的套接字错误。
  • 也许这个答案很有用。对我来说,它是:stackoverflow.com/questions/13844711/…

标签: python sockets client udp


【解决方案1】:

嗯,这似乎很明显。

  1. UDP没有连接,所以Client.connect错了
  2. 您将客户端地址存储在Server.connected dict 中。当客户端关闭时,没有人会在那里接收您发送的内容。

正确进行网络通信很难,因为 socket 库太低级(它是 C 套接字的薄包装器)。您的代码中遗漏了很多细节。我建议尝试更高级别的库,例如twisted。这里有一些 example on UDP 可以帮助您入门。

【讨论】:

  • udp 没有连接,但 connect 具有模拟它们的 udp 套接字的行为。
【解决方案2】:

问题远比看起来简单。使用 socket.recv() 而不是 socket.recvfrom() - 我在本地进行了此更改,然后您的代码就可以工作了。

【讨论】:

    【解决方案3】:

    首先,这可能是特定于平台的,您没有提及您正在运行的平台;但是,10054 是 WSAECONNRESET,所以我猜是某种 Windows 平台。

    其次,如前所述,与 UDP 没有连接。您在客户端中对Connect() 的调用只会导致客户端计算机上的网络代码允许您发起Send() 调用而不是SendTo() 调用,并且只需默认您在发出Send() 时发送数据的地址调用提供给Connect()的调用地址。

    第三,我很惊讶你收到的是WSAECONNRESET 而不是ERROR_PORT_UNREACHABLE;但是根本原因可能是相同的。如果您要发送到的端口上没有打开套接字,远程计算机上的 UDP 堆栈可能会发送 ICMP Port Unreachable 错误。因此,如果您的客户端发送数据然后关闭套接字,然后您的服务器将数据发送回客户端地址,您将获得一个无法访问的端口,并且某些版本的 Windows 可能会将其转换为连接重置错误...

    这些 ICMP 端口不可达错误的问题在于,它们是通过 Winsock 代码报告的,因为挂起的 UDP Recv/RecvFrom 调用失败。正如我解释here 和问题here 一样,UDP 堆栈显然知道生成无法访问的端口的地址,但它不会将该信息传递给调用者,因此您无法将这些消息映射到有用的东西。您可能在 Vista 之前的 Windows 版本上运行,并且 UDP 堆栈对该地址执行了一些有用的操作,并且它正在向正确的套接字报告错误,但不要打赌。

    最后你还是有问题; ICMP 端口不可达错误未可靠传递,因此如果您尝试将 UDP 数据发送到已消失的客户端,您无法确定是否会收到错误。恕我直言,这意味着即使它有时有效,您也不应该依赖它。

    您显然是在尝试在 UDP 之上构建某种面向连接的协议(否则为什么您的服务器会保留客户端的地址)。您必须做更多工作才能通过 UDP 创建一个可行的伪连接,首先要意识到的是,您知道客户端何时离开的唯一方法是设置您自己的超时并“断开”您的如果您在一段时间内没有收到他们的消息,则为伪连接。

    这当然不能回答你的问题;你如何避免异常。我希望你不能。异常的原因可能是来自Recv()RecvFrom() 调用的“失败”返回代码,python 网络代码可能会将所有此类失败返回转换为您的异常。

    【讨论】:

    • 感谢您的信息丰富和巨大的响应。 :) 我还怀疑我们无法避免异常。回答几个问题:我使用的是 Windows 7。另外,我们尝试将 IP 和套接字分开的原因是,如果我们有 5 个不同的客户端,它们都将具有单独的数据和帐户(服务器端)。在没有这些信息的情况下,服务器如何区分谁是谁?也许我们以错误的方式处理这个问题?
    • 你为什么不使用 TCP 呢?使用 TCP,您将拥有真正的连接,并在连接不再打开时发出通知。使用 UDP 意味着您必须建立自己的连接,因此您要么依赖客户端地址和端口不变(这可能没问题,但对于某些 NAT 可能不会),或者您在数据报中嵌入“连接 ID”。这样你就有了一个不可靠的面向连接的系统,但是你无法知道客户端什么时候离开;所以你需要超时......
    • 不幸的是,TCP 对于我们需要的东西来说太慢了,这是一个游戏。也许解决方案介于两者之间? TCP 处理登录信息,UDP 处理大部分发送到游戏服务器的数据。
    • 没有什么可以阻止您使用 UDP,但也许您应该看看已经在 UDP 上构建的各种协议,以将 TCP 的各种元素重新添加到组合中,同时提高性能;我在这里有一个问题:stackoverflow.com/questions/107668/… 列出了几个替代方案......请记住,有各种问题需要解决,其他人经常已经在那里完成了......(ENet 对游戏非常有用,但是我不确定是否有 python 绑定)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    • 2013-11-19
    • 1970-01-01
    • 2019-01-08
    相关资源
    最近更新 更多