一般来说,刚才描述的分支模型(派生服务器)在类unix平台上运行良好,但是会受一些隐藏的重要限制的影响:
在某些机器上,启动一个新的进程,在时间和空间资源方面的消耗特别大。
目前的os.fork调用在类似windows那样的非unix平台上不能运行。
分支会带来管理和捕获僵尸进程的所有方法—清理存在时间比它们的父进程少的子进程
解决所有这些难题的一个方案就是使用线程而不是进程。线程可以并行运行和共享全局内存。因为所有线程都运行在同一进程和内存空间中,所以它们自动共享它们之间传递的套接字,其核心类似于子进程继承套接字描述符的方式。然而,和进程不一样,线程的启动通常耗费不大,而且,如今可以运行在类unix机器和标准python下的windows上。此外,很多人认为线程是程序的简化形式—在退出时,子线程会静静的消失,不会留下在服务器上出没的僵尸进程。
__author__ = 'JianqingJiang'
# -*- coding: utf-8 -*-
import time,_thread as thread # use threading.Thread().start()
from socket import *
myHost = ''
myPort = 50007
sockobj = socket(AF_INET,SOCK_STREAM) # make a TCP socket object
sockobj.bind((myHost,myPort)) # bind it to server port number
sockobj.listen(5) # allow 5 pending connects
def now():
return time.ctime(time.time()) # current time on server
def handleClient(connection): # child process: reply,exit
time.sleep(5) # simulate a blocking activity
while True: # read , write a client socket
data = connection.recv(1024) # till eof when socket closed
if not data : break
reply = 'Echo=>%s at %s' % (data,now())
connection.send(reply.encode())
connection.close()
def dispatcher(): # listen until process killed
while True: # wait for next connection
connection,address = socket.accept() # pass to process for service
print('Server connected by', address, end = ' ' )
print('at',now())
thread.start_new_thread(handleClient,(connection,))
dispatcher()
该dispatcher程序把每个传入的客户端连接请求,指定到新派生的运行handleClient函数的线程中。因此,该服务器可以同时处理多个客户端,主要的调度程序循环可以迅速回到顶端,以便检查新到达的请求。最终的效果就是,新的客户端就不会因服务器繁忙而拒绝服务。
当线程运行函数返回时,线程会默默的退出;不同于进程的fork类型,我们在客户端处理函数上不调用类似os._exit的命令(它可以关闭进程中的所有线程,包括监听新连接的主循环!)。正因为如此,线程类似不仅具有可移植性,也更加简单。
socketserver模块定义了可以实现所有你可能会感兴趣的各种分支和线程服务器的类。这个模块可以实现服务器的并行处理客户端,以避免在长期运行的事务中,拒绝服务器新的请求。为了使用这个模块,只需创建所需的导入服务器对象类型,通过自身的回调方法传入处理器对象
__author__ = 'JianqingJiang'
# -*- coding: utf-8 -*-
import socketserver,time # get socket server,handler object
myHost = ''
myPort = 50007
sockobj = socket(AF_INET,SOCK_STREAM) # make a TCP socket object
sockobj.bind((myHost,myPort)) # bind it to server port number
sockobj.listen(5) # allow 5 pending connects
def now():
return time.ctime(time.time()) # current time on server
class MyClientHandler(socketserver.BaseRequestHandler):
def handleClient(self): # child process: reply,exit
print(self.client_address,now()) # show this client's address
time.sleep(5) # simulate a blocking activity
while True: # read , write a client socket
data = connection.recv(1024) # till eof when socket closed
if not data : break
reply = 'Echo=>%s at %s' % (data,now())
self.request.send(reply.encode())
self.request.close()
# make a threaded server,listen.handle clients forever
myaddr = (myHost,myPort)
server = socketserver.ThreadingTCPServer(myaddr,MyClientHandler)
server.serve_forever()
这个服务器于我们手写的线程服务器一样运行,但是相反,它侧重于服务的实现(定制的handle方法),而不是线程细节。它也以同样的方式运行。