2017-05-07 6 views
0

Socket.close()는 소켓에서 이미 실행중인 blocking socket.accept() 호출을 차단하지 않습니다.닫힌 유닉스 소켓에서 socket.accept()를 호출

파이썬 프로그램에 이미 닫혀있는 유닉스 도메인 소켓에서 blocking socket.accept() 호출 만 실행하는 여러 스레드가 있습니다. socket.accept() 호출을 중지 시키거나 예외를 발생시켜 이러한 스레드를 종료하려고합니다.

프로그램을 중단하지 않고 프로그램에 새 코드를로드하여이 작업을 수행하려고합니다. 따라서 이러한 스레드를 생성하거나 소켓을 닫은 코드를 변경하는 것은 옵션이 아닙니다.

이렇게 할 방법이 있습니까?

https://stackoverflow.com/a/10090348/3084431 유사하지만,이 솔루션은 내 코드에 대한 작업을 실 거예요 :

  1. 이 점은 사실이 아니다, 폐쇄는 동의에 예외를 발생하지 않습니다. shutdown하지만 스레드가 닫힐 때 더 이상 호출 할 수 없습니다.
  2. 더 이상이 소켓에 연결할 수 없습니다. 소켓이 닫혔습니다.
  3. 수락 호출이있는 스레드가 이미 실행 중이므로 변경할 수 없습니다.
  4. 같은 설명에 대한

3, 나는이 문제를 가지고 몇 가지 예제 코드를 작성했습니다. 이 코드는 작동 모두 파이썬 2와 파이썬 3

import socket 
import threading 
import time 

address = "./socket.sock" 

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 
sock.bind(address) 
sock.listen(5) 

def accept(): 
    print(sock.accept()) 

t = threading.Thread(target=accept, name="acceptorthread") 
t.start() 

sock.close() 

time.sleep(0.5) # give the thread some time to register the closing 

print(threading.enumerate()) # the acceptorthread will still be running 

내가해야 할 것은이 코드가 어떻게 든 셉터 스레드를 중지 할 수 있습니다 완료 후 내가 실행할 수있는 무언가이다.

+0

재고 소켓 문제는 무엇입니까? 'accept'가 "이미 실행 중"이라면, 새로운 연결을 수락하지 않기 전에 기술적으로 연결이 허용되었습니다. - 그래도 괜찮습니다. 모든 기존 클라이언트를 걷어차 고 싶다면 그렇게해야합니다. –

+1

당신은 결코이 일을 할 수 없을 것입니다. 당신의 코드는 이미 심각한 경쟁 조건을 가지고 있습니다 -'sock.close()'가'accept' 호출과 동시에 실행되면 어떻게 될까요? 파이썬의 소켓 객체는 스레드로부터 안전하지 못합니다. 다른 스레드가 스레드에 액세스하는 동안 한 스레드에서 상태를 수정할 수 없습니다. 실행하거나 차단할 준비가되는 것과는 반대로,'accept'가 실행 중인지를 확인할 수있는 방법은 절대적으로 없습니다. 따라서이 일은 안전하게 할 수 없습니다. –

+0

@ivan_pozdeev 클라이언트가 없다. 영원히 기다리고 스레드가 끝나지 못하도록 차단하는 'accept' 함수 일 뿐이다. @DavidSchwartz'sock.close'는'sock.accept'가 실행되는 동안 실제로 실행됩니다. 문제는 소켓이 닫혀 있지만 여전히'sock.accept'가 실행되고 있다는 것입니다. – Troido

답변

1

모든 수신기에 소켓이 닫혔다 고 알리는 메커니즘이 커널에 없습니다. 너 스스로 뭔가를 써야 해. 간단한 해결책은 소켓에 타임 아웃을 사용하는 것입니다

sock.settimeout(1) 

def accept(): 
    while True: 
     try: 
      print(sock.accept()) 
     except socket.timeout: 
      continue 
     break 

이제 "나쁜 기술자"예외가 발생합니다 .accept()에 (타임 아웃 후) 소켓에게 다음 전화를 닫을 때.

파이썬의 소켓 API는 스레드로부터 안전하지 않습니다. 멀티 스레드 환경에서는 모든 소켓 호출을 잠금 (또는 다른 동기화 메소드)으로 래핑하는 것이 좋습니다.

더 고급 (효율적인) 소켓을 select call으로 감싸는 것이 좋습니다. 소켓을 사용하려면 비 블로킹 모드 일 필요는 없습니다.

따라서 이러한 스레드를 생성하거나 소켓을 닫은 코드를 변경하는 것은 옵션이 아닙니다.

그렇다면, 당신은 운명을 저 지르게됩니다. 스레드에서 실행되는 코드를 변경하지 않으면 성취가 불가능합니다. 그것은 "차를 수리하지 않고 어떻게 깨진 차를 수리 할 수 ​​있습니까?"라고 묻는 것과 같습니다. 그런 일 없을거야, 친구.

+0

소켓이 닫히지 않으면 수정이 가능했을 것입니다. 닫히지 않으면'sock.shutdown'은'accept' 함수 에러를 만들어서 쓰레드를 멈출 수 있습니다. 여전히 소켓을 닫을 때 이런 식으로 할 수는 없습니다. 나는 누군가가 이것을하기 위해 트릭을 알기를 바랄 뿐이다. – Troido

0

selectors에서 "읽기 가능"결과를 제공 한 소켓에서만 .accept()을 호출해야합니다.그런 다음 accept는 이 필요하지 않습니다.

그러나 가짜 웨이크 업의 경우 수신 소켓은 O_NONBLOCK 모드 여야합니다.