我在python中编写一个简单的客户端服务器程序。在客户端程序中,我创建了两个线程(使用Python的线程模块),一个用于接收,一个用于发送。接收线程从服务器端连续接收字符串;而发送线程持续监听用户输入(使用raw_input())并将其发送到服务器端。这两个线程使用队列进行通信(本机同步,LIKE!)。
基本逻辑如下:
接收线程:
global queue = Queue.Queue(0)
def run(self):
while 1:
receive a string from the server side
if the string is QUIT signal:
sys.exit()
else:
put it into the global queue
发送帖子:
def run(self):
while 1:
str = raw_input()
send str to the server side
fetch an element from the global queue
deal with the element
正如您所看到的,在接收线程中,我有一个if条件来测试服务器是否已向客户端发送了“QUIT signal”。如果有,那么我希望整个程序停止。
这里的问题是,在大多数情况下,发送线程被“raw_input()”阻塞并等待用户输入。当它被阻塞时,从另一个线程(接收线程)调用“sys.exit()”将不会立即终止发送线程。发送线程必须等待用户输入内容并按下回车按钮。
有人能激励我如何解决这个问题吗?我不介意使用“raw_input()”的替代品。实际上我甚至不介意改变整个结构。
------------- ------------- EDIT
我在linux机器上运行它,我的Python版本是2.7.5
答案 0 :(得分:4)
你可以制作发送帖子daemonic:
send_thread = SendThread() # Assuming this inherits from threading.Thread
send_thread.daemon = True # This must be called before you call start()
如果剩下的唯一线程是守护进程,那么Python解释器就不会被阻止退出。因此,如果剩下的唯一主题是send_thread
,即使您在raw_input
上被阻止,您的程序也会退出。
请注意,这将突然终止发送线程,无论它做什么。如果它访问需要正确清理的外部资源或者不应该被中断(例如写入文件),这可能是危险的。如果您正在执行此类操作,请使用threading.Lock
进行保护,并且只有在您可以获取相同sys.exit()
时才从接收线程中调用Lock
。
答案 1 :(得分:1)
简短的答案是你不能。 input()
就像许多这样的输入命令一样正在阻塞,并且正在阻塞是否已杀死线程中的所有内容。有时您可以调用sys.exit()并使它根据操作系统运行,但这并不一致。有时您可以通过推迟到本地操作系统来终止程序。但是,那么您将不会在广泛的跨平台中使用。
如果有的话,您可能要考虑的是通过套接字来实现功能。因为与input()不同,我们可以进行超时,线程化并杀死某些东西。它还使您能够进行多个连接,甚至可以更广泛地接受连接。
import socket
import time
from threading import Thread
def process(command, connection):
print("Command Entered: %s" % command)
# Any responses are written to connection.
connection.send(bytes('>', 'utf-8'))
class ConsoleSocket:
def __init__(self):
self.keep_running_the_listening_thread = True
self.data_buffer = ''
Thread(target=self.tcp_listen_handle).start()
def stop(self):
self.keep_running_the_listening_thread = False
def handle_tcp_connection_in_another_thread(self, connection, addr):
def handle():
while self.keep_running_the_listening_thread:
try:
data_from_socket = connection.recv(1024)
if len(data_from_socket) != 0:
self.data_buffer += data_from_socket.decode('utf-8')
else:
break
while '\n' in self.data_buffer:
pos = self.data_buffer.find('\n')
command = self.data_buffer[0:pos].strip('\r')
self.data_buffer = self.data_buffer[pos + 1:]
process(command, connection)
except socket.timeout:
continue
except socket.error:
if connection is not None:
connection.close()
break
Thread(target=handle).start()
connection.send(bytes('>', 'utf-8'))
def tcp_listen_handle(self, port=23, connects=5, timeout=2):
"""This is running in its own thread."""
sock = socket.socket()
sock.settimeout(timeout)
sock.bind(('', port))
sock.listen(connects) # We accept more than one connection.
while self.keep_running_the_listening_thread:
connection = None
try:
connection, addr = sock.accept()
address, port = addr
if address != '127.0.0.1': # Only permit localhost.
connection.close()
continue
# makes a thread deals with that stuff. We only do listening.
connection.settimeout(timeout)
self.handle_tcp_connection_in_another_thread(connection, addr)
except socket.timeout:
pass
except OSError:
# Some other error.
if connection is not None:
connection.close()
sock.close()
c = ConsoleSocket()
def killsocket():
time.sleep(20)
c.stop()
Thread(target=killsocket).start()
这将启动端口23(远程登录)上设置的连接的侦听器线程,然后您进行连接,并将该连接传递给另一个线程。并且它启动了一个killsocket线程,该线程禁用了各个线程并让它们和平地死掉(用于演示目的)。但是,您无法在此代码内连接localhost,因为您需要input()
知道要发送到服务器的内容,这会重新产生问题。