2010-04-05 127 views
3

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

当然用UDP你不能,如果数据真的达到或知道它是封闭的,因为它不照顾(ATLEAST,它并不带来了一个例外)。但是,如果您发送数据并关闭数据,则会以某种方式返回数据(???),最终会给sock.recvfrom带来套接字错误。 [Errno 10054]现有连接被远程主机强制关闭。几乎看起来像来自连接的自动响应。

虽然这是罚款,并可以通过尝试来处理:除了:块(即使它降低了服务器一点点的性能)。问题是,我无法分辨这是从哪里来的,或者哪个socket被关闭了。无论如何找出'谁'(IP,套接字#)发送这个?这将是非常好的,因为我可以立即断开它们并将它们从数据中移除。有什么建议么?谢谢。

服务器:

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) 
+0

它似乎不是没有连接的UDP问题。你能显示导致问题的最小代码吗?请尝试隔离客户端和服务器上最小的代码块。 – 2010-04-05 04:45:51

+0

好吧,我用这个示例代码很容易地重新创建了它。我不确定我可以在这里粘贴所有的东西,所以我会使用粘贴位置。 服务器:http://paste-it.net/public/o230dad/ 客户端:http://paste-it.net/public/k194612/ 基本上,只需打开服务器和两个客户端。等待它循环got_data,然后关闭一个客户端。繁荣,在服务器的套接字错误。 – Charles 2010-04-05 05:24:05

+0

也许这个回答很有用。对我来说,这是:http://stackoverflow.com/questions/13844711/udp-hole-punching-c-winsock – 2016-03-29 15:12:50

回答

1

嗯,这似乎是显而易见的。

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

因为socket库太低级别(它是C套接字的薄包装),所以很难获得网络通信。代码中遗漏了很多细节。我建议尝试更高级别的库,如twisted。这里有一些example on UDP让你开始。

+0

udp没有连接,但连接有模仿它们的udp套接字的行为。 – xaxxon 2016-10-20 13:38:12

2

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

7

首先这可能是特定的平台,你不提,你运行的平​​台;然而,10054是WSAECONNRESET所以我猜测某种Windows平台。

其次如前面所指出的存在与UDP没有任何联系。您在客户端Connect()调用只是使您的客户端机器上的网络代码,让你发起Send()呼叫,而不是SendTo()电话和简单的默认要发送数据时发出Send()调用提供给呼叫的地址到地址到Connect()

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

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

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

你显然是试图在UDP之上构建某种面向连接的协议(为什么你的服务器会保留客户端的地址)。你必须做更多的事情才能通过UDP创建一个可行的伪连接,并且要实现的第一件事之一是你可以知道客户端何时离开的唯一方法是设置你自己的超时并断开连接伪连接,如果你在一段时间内没有收到它们的消息。

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

+0

感谢您提供丰富翔实的回复。 :)我也怀疑我们无法避免这个例外。回答几个问题:我正在使用Windows 7.另外我们试图分离IP和套接字#的原因是,如果我们有5个不同的客户端,它们都将具有单独的数据和帐户(服务器端)。如果没有这些信息,服务器如何区分谁是谁,数据明智?也许我们正在接近这个错误的方式? – Charles 2010-04-05 16:17:13

+0

为什么你不使用TCP?通过TCP,您可以获知连接何时不再打开的实际连接。保持与UDP意味着你必须建立自己的连接,所以要么依赖于客户端地址和端口不变(这可能是好的,但可能不是与一些NAT),或者你在数据报中嵌入了一个“连接ID”。因为你有一个不可靠的面向连接的系统,但你无法知道客户何时离开;所以你需要超时... – 2010-04-05 18:10:46

+0

不幸的是TCP对我们所需要的太慢了,这是一场游戏。也许解决方案是介于两者之间的?用于处理登录信息的TCP和用于处理发送到游戏服务器的大部分数据的UDP。 – Charles 2010-04-05 18:39:44