2010-04-05 12 views
3

현재 UDP 및 Python 소켓 모듈을 사용하는 데 문제가 있습니다. 우리는 서버와 클라이언트를 보유하고 있습니다. 이 문제는 사용자에게 데이터를 보낼 때 발생합니다. 클라이언트 크래시, ISP 연결 해제 또는 기타 부적절한 방법을 통해 사용자가 서버에 대한 연결을 종료했을 수 있습니다. 따라서 닫힌 소켓에 데이터를 보낼 수 있습니다.UDP 데이터에서 Python 소켓 오류가 발생합니다. (10054)

물론 UDP를 사용하면 데이터가 실제로 도달했는지 또는 닫혀 있는지 알 수 없으며 상관하지 않으므로 (적어도 예외는 발생하지 않음). 그러나 데이터를 보내고 닫으면 데이터가 어떻게 든 돌아오고 (???) 결국 sock.recvfrom에 소켓 오류가 발생합니다. [Errno 10054] 기존 연결이 원격 호스트에 의해 강제로 닫혔습니다. 거의 자동으로 연결에서 응답을 보인다.

괜찮 으면 좋겠지 만 try : except : block (서버의 성능을 조금 떨어 뜨린 경우에도)으로 처리 할 수 ​​있습니다. 문제는이게 누구에게서 왔는지, 어떤 소켓이 닫혀 있는지를 알 수 없다는 것입니다. 어쨌든 'who'(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) 
+0

연결이없는 UDP 문제는 아닙니다. 당신에게 문제를 일으키는 최소한의 코드를 보여줄 수 있습니까? 가장 작은 코드 조각을 클라이언트와 서버에서 격리 시키십시오. –

+0

그래,이 예제 코드를 사용하여 상당히 쉽게 다시 만들었다. 붙여 넣기 지점을 사용할 수 있도록 여기에 붙여 넣을 수 있는지 잘 모르겠습니다. 서버 : http://paste-it.net/public/o230dad/ 클라이언트 : http://paste-it.net/public/k194612/ 기본적으로 서버와 클라이언트 두 개만 엽니 다. 루프가 got_data 될 때까지 기다린 다음 클라이언트 하나를 닫으십시오. 붐, 서버의 소켓 오류. – Charles

+0

아마도이 대답이 유용 할 것입니다. 나를 위해, 그것은 : http://stackoverflow.com/questions/13844711/udp-hole-punching-c-winsock –

답변

1

음, 분명한 것 같다.

  1. UDP 연결을 가지고 있지 않기 때문에 Client.connect 당신은 Server.connecteddict에서 클라이언트의 주소를 저장하고
  2. 잘못된 것입니다. 클라이언트가 닫히면 아무도 당신이 보내는 것을받을 수 없습니다.

socket 라이브러리가 너무 낮음 (C 소켓을 감싸는 얇은 포장지이기 때문에) 네트워크 통신 권한을 얻는 것이 어렵습니다. 코드에 많은 세부 사항이 빠졌습니다. twisted과 같이 더 높은 수준의 라이브러리를 사용하는 것이 좋습니다. 시작하려면 약간의 example on UDP이 필요합니다.

+0

udp는 연결을 가지고 있지 않지만 connect는 그들을 에뮬레이션하는 udp 소켓에 대한 동작을합니다. – xaxxon

2

문제는 보이는 것보다 훨씬 간단합니다. socket.recvfrom() 대신 socket.recv()를 사용하십시오.이 변경을 로컬에서 수행 했으므로 코드가 작동합니다.

7

먼저이 플랫폼은 특정 플랫폼 일 수 있으며 실행중인 플랫폼은 언급하지 않았습니다. 그러나 10054는 WSAECONNRESET입니다. 그래서 나는 어떤 종류의 Windows 플랫폼을 추측하고 있습니다.

둘째 이전에 지적한대로 UDP와 연결되지 않습니다. 클라이언트에서 을 호출하면 클라이언트 컴퓨터의 네트워킹 코드가 SendTo() 호출이 아닌 Send() 호출을 시작하도록 허용하고 호출에 제공된 주소로 Send() 호출을 발행 할 때 데이터를 전송하는 주소 만 간단히 설정할 수 있습니다 Connect().

셋째로 나는 WSAECONNRESET이 아니고 ERROR_PORT_UNREACHABLE이 아니라는 것에 놀랐습니다. 근본적인 이유는 아마도 동일합니다. 전송하려는 포트에 소켓이 열려 있지 않으면 원격 시스템의 UDP 스택이 ICMP 포트 도달 불가능 오류를 전송할 가능성이 높습니다.따라서 클라이언트가 데이터를 보낸 다음 소켓을 닫은 다음 서버가 클라이언트 주소로 데이터를 보내면 연결할 수없는 포트가 생기고 일부 Windows 버전이 연결 재설정 오류로 변환 될 수 있습니다.

이러한 ICMP 포트 도달 할 수없는 오류의 문제점은 보류중인 UDP Recv/RecvFrom 호출이 실패하여 Winsock 코드를 통해보고된다는 것입니다. 내가 here과 질문 : here을 설명 했으므로 UDP 스택은 포트에 도달 할 수없는 주소를 알고 있지만 호출자에게 해당 정보를 전달하지 않으므로이 메시지를 유용한 것으로 매핑 할 수있는 방법은 없습니다. Vista 이전 버전의 Windows에서 실행 중일 수 있으며 UDP 스택이 주소와 관련된 유용한 정보를 제공하고 있으며 올바른 소켓으로 오류를보고하고 있지만 내기하지는 않았습니다.

마지막으로 문제가 발생합니다. ICMP 포트 도달 불가능 오류가 안정적으로 전달되지 않으므로 멀리 떨어진 클라이언트에게 UDP 데이터를 보내려고하면 오류가 발생한다는 것을 확실히 알 수 없습니다. IMHO 이것은 가끔씩 작동하더라도 당신이 그것에 의지해서는 안된다는 것을 의미합니다.

UDP를 기반으로 일종의 연결 지향 프로토콜을 구축하려고 시도하고 있습니다 (서버가 클라이언트의 주소를 보유해야하는 이유는 무엇입니까?). UDP를 통해 실용적인 의사 연결을 생성하려면 훨씬 더 많은 노력을 기울여야하며, 클라이언트가 사라 졌을 때 알 수있는 유일한 방법은 자신의 시간 제한을 설정하고 연결을 끊는 것입니다 의사 연결을 일정 시간 내에 들지 않으면 의사 연결을 시도합니다.

물론이 질문에 대한 답변을 제공하지 않습니다. 예외를 어떻게 피할 수 있습니까? 나는 네가 그럴 수 없다고 기대한다. 예외의 원인은 Recv() 또는 RecvFrom() 호출의 '실패'리턴 코드 일 수 있으며 파이썬 네트워크 코드는 이러한 모든 실패 리턴을 예외로 변환하고 있습니다.

+0

유익하고 거대한 응답에 감사드립니다. :) 나는 또한 우리가 예외를 피할 수 없다고 의심했다. 몇 가지 질문에 답하기 위해서 : 나는 Windows 7을 사용하고있다. 또한 IP와 소켓 #을 분리하려고하는 이유는 5 개의 다른 클라이언트가 있다면 그들 모두는 별도의 데이터와 계정 (서버 측)을 가질 것이다. 서버가 정보가없는 사람, 데이터 현자를 누가 구분할 수 있습니까? 어쩌면 우리는 이것을 잘못된 방향으로 접근하고있을 것인가? – Charles

+0

왜이 용도로 TCP를 사용하지 않습니까? TCP를 사용하면 연결이 더 이상 열리지 않을 때의 알림과 실제 연결을 유지할 수 있습니다. UDP를 유지한다는 것은 자신 만의 연결을 만들어야한다는 것을 의미합니다. 따라서 클라이언트 주소와 포트를 변경하지 않아도됩니다 (일부 NAT에서는 정상이지만 일부 NAT에서는 그렇지 않을 수도 있음). 또는 데이터 그램에 '연결 ID'를 포함시킵니다. 이를 통해 신뢰할 수없는 연결 지향 시스템을 갖지만 클라이언트가 언제 사라지는 지 알 방법이 없습니다. 그래서 시간 초과가 필요합니다 ... –

+0

불행히도 TCP는 우리가 필요로하는 것에 비해 너무 느려서 게임입니다. 어쩌면 해결책은 중간에 있을까요? 게임 서버에 보내는 대부분의 데이터를 처리하기 위해 로그인 정보와 UDP를 처리하기위한 TCP. – Charles

관련 문제