2010-05-26 13 views
10

Tôi có một chuỗi chính chờ kết nối. Nó sinh ra các chuỗi khách hàng sẽ lặp lại phản hồi từ máy khách (telnet trong trường hợp này). Nhưng nói rằng tôi muốn đóng tất cả các ổ cắm và tất cả các chủ đề sau một thời gian, như sau 1 kết nối. Làm thế nào tôi sẽ làm gì? Nếu tôi làm clientSocket.close() từ chủ đề chính, nó sẽ không ngừng thực hiện recv. Nó sẽ chỉ dừng lại nếu tôi lần đầu tiên gửi một cái gì đó thông qua telnet, sau đó nó sẽ không làm tiếp tục gửi và recvs.Làm cách nào để hủy bỏ socket.recv() khỏi chuỗi khác trong Python

đang nhìn tôi như thế này:

# Echo server program 
import socket 
from threading import Thread 
import time 

class ClientThread(Thread): 
    def __init__(self, clientSocket): 
      Thread.__init__(self) 
      self.clientSocket = clientSocket 

    def run(self): 
      while 1: 
        try: 
          # It will hang here, even if I do close on the socket 
          data = self.clientSocket.recv(1024) 
          print "Got data: ", data 
          self.clientSocket.send(data) 
        except: 
          break 

      self.clientSocket.close() 

HOST = '' 
PORT = 6000 
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
serverSocket.bind((HOST, PORT)) 
serverSocket.listen(1) 

clientSocket, addr = serverSocket.accept() 
print 'Got a new connection from: ', addr 
clientThread = ClientThread(clientSocket) 
clientThread.start() 

time.sleep(1) 

# This won't make the recv in the clientThread to stop immediately, 
# nor will it generate an exception 
clientSocket.close() 
+0

Bạn không thể làm điều đó với chủ đề vì CPython có Khóa thông dịch toàn cầu. http://docs.python.org/c-api/init.html#threads – badp

Trả lời

0

Tôi không chắc chắn nhưng bạn có lẽ có thể nhìn vào cặp ổ cắm

+0

Tôi không nghĩ điều đó sẽ giúp tôi. Tôi nghĩ rằng việc sử dụng socketpair chủ yếu cho IPC. Tôi không muốn giao tiếp với các chủ đề, mà sẽ làm cho các chủ đề khách hàng chờ đợi cho đầu vào từ các chủ đề chính. Nó không nghĩ rằng sẽ giải quyết vấn đề của tôi. –

5

Tôi không biết nếu nó có thể làm những gì bạn đang hỏi, nhưng nó không cần thiết. Chỉ cần không đọc từ ổ cắm nếu không có gì để đọc; sử dụng select.select để kiểm tra ổ cắm cho dữ liệu.

thay đổi:

data = self.clientSocket.recv(1024) 
print "Got data: ", data 
self.clientSocket.send(data) 

một cái gì đó như thế này:

r, _, _ = select.select([self.clientSocket], [], []) 
if r: 
    data = self.clientSocket.recv(1024) 
    print "Got data: ", data 
    self.clientSocket.send(data) 

EDIT: Nếu bạn muốn bảo vệ chống lại khả năng rằng các ổ cắm đã bị đóng cửa, bắt socket.error.

do_read = False 
try: 
    r, _, _ = select.select([self.clientSocket], [], []) 
    do_read = bool(r) 
except socket.error: 
    pass 
if do_read: 
    data = self.clientSocket.recv(1024) 
    print "Got data: ", data 
    self.clientSocket.send(data) 
+1

Nhưng điều tương tự cũng sẽ xảy ra với lựa chọn. Nếu tôi đóng xuống socket, nó sẽ phàn nàn về bộ mô tả tập tin xấu khi thực hiện select.select(). Tôi thấy rằng giải pháp của bạn đã xuất hiện trước khi tôi đăng giải pháp của mình. Bạn nghĩ gì về cách tôi giải quyết nó ở đó, sử dụng timeouts? –

+0

Tôi đã thử tương tự với chọn ngay bây giờ. Nó hoạt động tốt như với timeouts, nhưng chỉ khi tôi đóng socket ngay lập tức sau khi tôi bắt đầu thread. Nếu tôi làm một time.sleep (1) nó sẽ thất bại. –

2

Tôi đã tìm thấy giải pháp bằng cách hết thời gian chờ. Điều đó sẽ gián đoạn recv (thực sự trước khi thời gian chờ đã hết hạn mà là tốt đẹp):

# Echo server program 
import socket 
from threading import Thread 
import time 


class ClientThread(Thread): 
    def __init__(self, clientSocke): 
     Thread.__init__(self) 
     self.clientSocket = clientSocket 

    def run(self): 
     while 1: 
      try: 
       data = self.clientSocket.recv(1024) 
       print "Got data: ", data 
       self.clientSocket.send(data) 
      except socket.timeout: 
       # If it was a timeout, we want to continue with recv 
       continue 
      except: 
       break 

     self.clientSocket.close() 

HOST = '' 
PORT = 6000 
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
serverSocket.bind((HOST, PORT)) 
serverSocket.listen(1) 

clientSocket, addr = serverSocket.accept() 
clientSocket.settimeout(1) 

print 'Got a new connection from: ', addr 
clientThread = ClientThread(clientSocket) 
clientThread.start() 

# Close it down immediatly 
clientSocket.close() 
+0

Trong C# có một phương pháp rất hữu ích như ** WaitAny ** mà tôi đã sử dụng cho chính xác mục đích này (để dừng dữ liệu chờ đợi từ ổ cắm nếu có một sự kiện khác để làm việc với). Python thực sự thiếu chức năng như vậy: ( Bình chọn cho biến thể của bạn Nhưng tôi nghĩ nó tải CPU một chút nếu có nhiều chủ đề (và thời gian chờ chỉ dài một giây) ... – sunsay

12

Tôi biết đây là một chủ đề cũ và Samuel có thể cố định vấn đề của mình một thời gian dài trước đây. Tuy nhiên, tôi đã có cùng một vấn đề và đã xem qua bài đăng này trong khi google'ing. Tìm thấy một giải pháp và nghĩ rằng nó là đáng giá để thêm.

Bạn có thể sử dụng phương pháp tắt trên lớp ổ cắm. Nó có thể ngăn chặn thêm gửi, nhận hoặc cả hai.

socket.shutdown (socket.SHUT_WR)

Các ngăn ngừa trong tương lai trên gửi, làm ví dụ.

See Python docs for more info.

+0

Làm việc cho tôi, cảm ơn bạn! –

1

Tôi phải xin lỗi vì nhận xét bên dưới. Nhận xét trước đó của @Matt Anderson hoạt động. Tôi đã phạm sai lầm khi thử nó ra dẫn đến bài viết của tôi dưới đây.

Sử dụng thời gian chờ không phải là giải pháp tốt. Dường như việc thức dậy ngay lập tức và sau đó trở lại giấc ngủ không phải là vấn đề lớn, nhưng tôi đã thấy nó ảnh hưởng rất lớn đến hiệu suất của một ứng dụng. Bạn có một hoạt động mà hầu hết các phần muốn chặn cho đến khi dữ liệu có sẵn và do đó ngủ mãi mãi. Tuy nhiên, nếu bạn muốn hủy bỏ vì một số lý do, như tắt ứng dụng của bạn, thì mẹo là cách thoát ra. Đối với ổ cắm, bạn có thể sử dụng lựa chọn và nghe trên hai ổ cắm. Cái chính của bạn, và một cái tắt máy đặc biệt. Tạo ra một tắt máy mặc dù là một chút đau đớn. Bạn phải tạo ra nó. Bạn phải lấy ổ cắm nghe để chấp nhận nó. Bạn phải theo dõi cả hai đầu của đường ống này. Tôi có cùng một vấn đề với lớp Queue được đồng bộ hóa. Tuy nhiên, bạn có thể ít nhất chèn một đối tượng giả vào hàng đợi để đánh thức get().Điều này yêu cầu đối tượng giả không giống như dữ liệu bình thường của bạn. Đôi khi tôi muốn Python có thứ gì đó giống như WaitForMultipleObjects của Windows API.