2011-09-07 3 views
9

파이썬 스크립트를 사용하여 UDP 패킷을 서버로 보내 서버에 알림을 수신하도록 스크립트를 등록합니다. 프로토콜을 사용하려면 이러한 IP 주소와 포트를 보내야합니다. 따라서이 주소는 서버의 네트워크에서 연결할 수 있어야합니다.Python : IP 주소를 특정 원격 IP 주소로 보내는 데 사용되는 로컬 IP 주소 가져 오기

솔루션은 Windows XP 이상 및 Mac OS X에서 작동해야합니다. 이는 내 개발 플랫폼이기 때문에.

첫 번째 단계는 내 인터페이스의 IP 주소를 얻는 것입니다. 현재 컴퓨터에서 호스트 이름을 확인하기 위해 일반적으로 제안되는 방법을 사용하고 있습니다.

def get_local_address(): 
    return socket.gethostbyname(socket.gethostname()) 

이것은 가끔씩 만 작동합니다. 현재 172.16.249.1을 반환하는데, 이는 VM웨어가 사용하는 가상 인터페이스의 주소이기 때문에 문제가됩니다.

대부분의 경우 가장 적합한 기본 인터페이스의 IP 주소를 얻는 방법이 훨씬 더 좋습니다.

TCP와 같은 연결 지향 프로토콜로 서버에 연결하는 데 사용되는 실제 IP 주소를 얻는 방법이 더 좋을 것입니다. 나는 실제로 그 주소를 얻을 수 있지만 연결을 열려고 시도하지 않고 할 수

def get_address_to_connect_to(server_addr): 
    non_open_port = 50000 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

    try: 
     s.connect((server_addr, non_open_port)) 
    except socket.error: 
     pass 
    else: 
     s.close() # just in case 

    return s.getsockname()[0] 

일부 TCP 타임 아웃은 서버에 연결할 수 없거나 사이에서 악한 방화벽 ICMP 차단되는 경우에 도달 할 때까지이 차단됩니다 패킷. 그 정보를 얻는 더 좋은 방법이 있습니까?

답변

11

나는 한 번 같은 문제를 해결해야했고 성공하지 못하면서 더 좋은 방법을 찾으려고 많은 시간을 보냈다. 나는 또한 gethostname으로 시작했지만, 당신이했던 것처럼 항상 올바른 결과를 리턴하지는 않는다는 것을 발견했다. 호스트 파일에 문제가있는 경우 예외를 던질 수도있다.

플랫폼간에 안정적으로 작동하는 유일한 해결책은 연결을 시도하는 것입니다. 코드 개선 사항 중 하나는 TCP 대신 UDP (SOCK_STREAM 대신 SOCK_DGRAM)를 사용하는 것입니다. 이렇게하면 연결 시간 초과가 방지되고 함수는 즉시 완료됩니다.

방화벽을 실행하는 클라이언트 위치에이 코드를 배포하는 경우이 검사를 수행한다는 사실을 문서화해야합니다. 그렇지 않으면 포트 50000에 대한 아웃 바운드 연결에 대한 방화벽 경고가 표시 될 수 있으며 그 목적을 이해하지 못하고 버그로 간주 될 수 있습니다.

편집 :

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

try: 
    s.connect((host, 9)) 
    client = s.getsockname()[0] 
except socket.error: 
    client = "Unknown IP" 
finally: 
    del s 
return client 

당신이주의 getsockname을 호출하기 전에 소켓을 닫는 있기 때문에 당신이 0.0.0.0을 받고 있다고 생각 : 여기에 대략 내가 사용하는 코드입니다. 또 다른 팁은 포트 9를 사용하는 것입니다. 그것은 RFC863 UDP 버려진 포트이며, 따라서 임의의 높은 번호가 매겨진 포트보다 약간 이상하지 않습니다.

+0

아, 달콤한 공감! 'SOCK_DGRAM'으로, 거의 완벽 할 것입니다! 그러나''.getsockname()'은 ('0.0.0.0', 51770)'을 반환합니다. 나는's = socket.socket (socket.AF_INET, socket.SOCK_DGRAM);을 시도했다. s.sendto (b'hello ', ('1.2.3.4 ', 50000)); s.getsockname() [0]'. 대답에 코드를 추가하면 좋을 것입니다! – Feuermurmel

+0

답변을 수정하십시오. – DNS

+0

완벽하게 작동합니다. 심지어 내가 모르는 [폐기 프로토콜] (http://en.wikipedia.org/wiki/Discard_Protocol)을 사용합니다! 나는'del s'을'with' 문으로 대체했습니다. – Feuermurmel

0

송신 측에서 IP 주소를 알아야합니까?

클라이언트는 UDP '레지스터'를 보낸 후 (모든 인터페이스에서) 가입하려는 데이터를 수신 대기 할 수 없으며 서버가 데이터 그램의 발신 주소로 데이터를 전송하기 시작합니까?

+0

이교도! 이 서비스는 내 인생을 더 쉽게 만들어주는 많은 일을 할 수있었습니다. 그러나 프로젝트의 일부이기도 한 SIP 구현에도 같은 문제가 나타납니다.그것은'REGISTER' 요청의'Contact' 헤더에 자신의 IP 주소를 보내야합니다. – Feuermurmel