我在您的代码中看到了一些行为异常的原因。
- Ctrl + C在主线程中导致“ KeyboardInterrupt”异常。因此,您应该在那里处理。
- 您的套接字处于阻止模式。这将导致多个套接字函数阻塞调用线程,直到该函数返回为止。在此状态下,线程无法对任何终止事件作出反应。
- 正如您已经说过的:线程的run()函数中的无穷循环是…确实无穷无尽。因此线程执行永无休止(至少并非没有意外的异常)。您应该使用某种同步对象,例如threading.Event对象,以便能够从外部告知线程它应该终止。
- 我不鼓励在主线程中使用raw_input()。想象一下,当您有多个喊线程时会发生什么。
- 为什么在Shout类中发送了消息后,为什么总是关闭并重新连接套接字?由于设置成本,仅在特殊情况下才应重新建立网络连接。
- 如果没有用于通信的帧协议,那么当recv()函数返回时,您将永远无法期望接收到另一主机发送的所有数据。
- 线程对象的start()函数不返回值或对象。因此,保存返回值(= None)没有多大意义。
- 您永远都不能期望send()函数传输所有传递的数据。因此,您必须检查函数的结果并适当地处理并非所有字节都已真正传输的情况。
- 要学习线程,肯定比网络通信要解决更好的问题,因为该主题本身确实很复杂。
除了所有这些以外,这是我尝试解决的方法。仍有很多可以改进的地方。您也应该考虑Mark
Tolonen的答案,因为肯定提供了SocketServer类可以简化处理此类事件的几件事。但是您也应该继续学习基础知识。
#!/usr/bin/env pythonimport threadingimport socketimport timeimport errnoclass StoppableThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.stop_event = threading.Event() def stop(self): if self.isAlive() == True: # set event to signal thread to terminate self.stop_event.set() # block calling thread until thread really has terminated self.join()class Accept(StoppableThread): def __init__(self, port): StoppableThread.__init__(self) self.port = port self.threads = [] def run(self): # handle connection acception conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.bind(('', self.port )) conn.listen(5) # set socket timeout to ~10ms conn.settimeout(0.01) while self.stop_event.is_set() == False: try: csock, caddr = conn.accept() # spawn a new thread to handle the client connection listen_thread = Listen(csock, caddr) self.threads.append(listen_thread) listen_thread.start() except socket.timeout: # socket operation timeout # clear all terminated threads from thread list for thread in self.threads: if thread.isAlive() == False: self.threads.remove(thread) self.stop_threads() def stop_threads(self): # stop all running threads for listen_thread in self.threads: if listen_thread.isAlive() == True: listen_thread.stop() self.threads = []class Listen(StoppableThread): def __init__(self, csock, caddr): StoppableThread.__init__(self) self.csock = csock self.caddr = caddr self.csock.setblocking(False) def run(self): while self.stop_event.is_set() == False: try: recv_data = self.csock.recv(250) if len(recv_data) > 0: print str(self.caddr)+": " + recv_data self.csock.send("got it") else: # connection was closed by foreign host self.stop_event.set() except socket.error as (sock_errno, sock_errstr): if (sock_errno == errno.EWOULDBLOCK): # socket would block - sleep sometime time.sleep(0.1) else: # unexpected / unhandled error - terminate thread self.stop_event.set() channel.close()class Shout(StoppableThread): def __init__(self, sport): StoppableThread.__init__(self) self.sport = sport def run(self): while self.stop_event.is_set() == False: try: address = raw_input("who u talking to? ") conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect((address, self.sport)) break except socket.error: # handle connection problems print "can't connect to "+ str(address) except: # exit thread in case of an unexpected error self.stop_event.set() while self.stop_event.is_set() == False: try: # chat loop: send messages to remote host print "what to send? :", msg = raw_input() # beware: send() function may block indefinitly here and it might not send all bytes as expected !! conn.send(msg) except: # exit thread in case of an unexpected error self.stop_event.set() # close socket before thread terminates conn.close()def main(): do_exit = False server_port = 2727 # start server socket thread accept = Accept(server_port) accept.start() # start transmitting client socket thread shout = Shout(server_port) shout.start() while do_exit == False: try: # sleep some time time.sleep(0.1) except KeyboardInterrupt: # Ctrl+C was hit - exit program do_exit = True # stop all running threads shout.stop() accept.stop() # exit main program after all threads were terminated gracefullyif __name__ == "__main__": main()


