接收 UDP 数据时出现 Python 套接字错误。(10054)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2576926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-04 00:58:18  来源:igfitidea点击:

Python socket error on UDP data receive. (10054)

pythonsocketsclientudp

提问by Charles

I currently have a problem using UDP and Python socket module. We have a server and clients. The problem occurs when we send data to a user. It's possible that user may have closed their connection to the server through a client crash, disconnect by ISP, or some other improper method. As such, it is possible to send data to a closed socket.

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

Of course with UDP you can't tell if the data really reached or if it's closed, as it doesn't care (atleast, it doesn't bring up an exception). However, if you send data and it is closed off, you get data back somehow (???), which ends up giving you a socket error on sock.recvfrom. [Errno 10054] An existing connection was forcibly closed by the remote host. Almost seems like an automatic response from the connection.

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

Although this is fine, and can be handled by a try: except: block (even if it lowers performance of the server a little bit). The problem is, I can't tell who this is coming from or what socket is closed. Is there anyway to find out 'who' (ip, socket #) sent this? It would be great as I could instantly just disconnect them and remove them from the data. Any suggestions? Thanks.

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

Server:

服务器:

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()

Client:

客户:

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)

回答by Len Holgate

Firstly this is possibly platform specific and you don't mention the platform that you're running on; however, 10054 is WSAECONNRESETso I'm guessing a Windows platform of some kind.

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

Secondly as previously pointed out there is no connection with UDP. Your call to Connect()in the client simply causes the networking code on your client machine to allow you to initiate Send()calls rather than SendTo()calls and simply default the address that you are sending data to when you issue Send()calls to the address supplied to the call to Connect().

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

Thirdly I'm surprised that you're getting WSAECONNRESETand not ERROR_PORT_UNREACHABLE; however the underlying reason is probably the same. The UDP stack on the remote machine will likely be sending a ICMP Port Unreachable error if there's no socket open on the port that you are sending to. So, if your client sends data and then closes the socket and then your server sends data back to the client address you'll get a port unreachable and some versions of windows might be translating that into a connection reset error...

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

The problem with these ICMP port unreachable errors, is that they're reported via the Winsock code by failing a pending UDP Recv/RecvFrom call. As I explain hereand question herethe UDP stack obviously knows the address that generated the port unreachable but it doesn't pass that information on to the caller so there's nothing that you can do to map these messages to something useful. It's possible that you're running on a version of Windows prior to Vista and the UDP stack is doing something useful with the address and it IS reporting the error to the correct socket, but don't bet on it.

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

Finally you have a problem anyway; the ICMP port unreachable error isn't delivered reliably so you can't know for sure that you WILL get an error if you try and send UDP data to a client that has gone away. IMHO this means that you shouldn't rely on it even if it works sometimes.

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

You are obviously attempting to build some kind of connection oriented protocol on top of UDP (why else would your server hold on to the addresses of the clients). You'll have to do a lot more to create a viable pseudo connection over UDP and one of the first things to realise is that the only way you can know when a client has gone away is to set your own timeout and 'disconnect' your pseudo connection if you don't hear from them within a certain period of time.

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

Of course this doesn't answer your question; how do you avoid the exception. I expect you can't. The cause of the exception is likely a 'failure' return code from a Recv()or RecvFrom()call and the python network code is probably converting all such failure returns into exceptions for you.

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

回答by Lee

The problem is far simpler than it looks. Use socket.recv() rather than socket.recvfrom() - I made this change locally and your code then works.

问题远比看起来简单。使用 socket.recv() 而不是 socket.recvfrom() - 我在本地做了这个改变,然后你的代码就可以工作了。

回答by nosklo

Well, it seems obvious.

嗯,这似乎很明显。

  1. UDP doesn't have connections, so Client.connectis wrong
  2. You're storing the client address in the Server.connecteddict. When the client is closed, nobody will be there to receive what you're sending.
  1. UDP 没有连接,所以Client.connect是错误的
  2. 您将客户端地址存储在Server.connecteddict. 当客户端关闭时,没有人会在那里接收您发送的内容。

Getting network communication right is hard, since socketlibrary it is too low-level (it is a thin wrapper around C sockets). A lot of details have been left out on your code. I suggest trying a higher level library, like twisted. Here's some example on UDPto get you started.

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