正如前面的socket模块部分看到的一样,写一个简单套接字服务器不是很难,如果想实现超出继承的应用,最好寻求一些帮助,socketserver模块是标准库中很多服务器框架的基础,这些服务器架构包括BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXMLRPCServer、DocXMLRPCServer,所有的这些服务器框架都为基础服务器增加了特定功能;
socketserver内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求
ThreadingTCPServer(多线程,真并发)
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
使用ThreadingTCPServer:
- 创建一个继承自 SocketServer.BaseRequestHandler 的类
- 类中必须定义一个名称为 handle 的方法
- 启动ThreadingTCPServer
用socketserver对ssh程序做修改,实现多用户同时操作互不影响
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #-Author-Lian
4
5 #scoketserver
6
7 import socketserver,os
8
9 class Myserver(socketserver.BaseRequestHandler):
10 def handle(self):
11 while True:
12 conn = self.request
13 # conn,add = server.accept()
14 while True:
15 print("开始收")
16 client_data = conn.recv(1024)
17 client_data = client_data.decode("utf-8")
18 if client_data == "exit": #收到exit 退出
19 break
20 send_data = os.popen(client_data).read() #执行命令结果,要发送的数据
21 send_data = send_data.encode("utf-8") #转换为bytes类型
22
23 length = str(len(send_data)) #统计发送数据的长度
24 conn.sendall(length.encode("utf-8")) #长度以bytes类型发送过去
25
26 return_value = conn.recv(1024)
27 return_value = return_value.decode("utf-8")
28
29 if return_value == "start":
30 if not send_data: # 如果执行结果为空,表示命令不存在
31 conn.sendall((client_data +"命令不存在").encode("utf-8"))
32 else:
33 conn.sendall(send_data)
34 conn.close()
35
36 if __name__ == '__main__':
37 server = socketserver.ThreadingTCPServer(("127.0.0.1",8888),Myserver)
38 server.serve_forever()
39
40 ssh 服务端多用户同时连接
ssh 服务端多用户同时连接
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #-Author-Lian
4
5 #ssh client
6
7 import socket
8
9 ip_port = ("127.0.0.1",8888)
10 client = socket.socket()
11 client.connect(ip_port)
12
13 while True:
14 cmd = input("->>").strip()
15 if not cmd: #空字符 重新输入
16 continue
17 client.sendall(cmd.encode("utf-8")) #要执行的命令发送过去
18 if cmd == "exit": #如果为exit 退出连接
19 break
20
21 length = client.recv(1024) #数据长度
22 length = length.decode("utf-8")
23 length = int(length) #长度转换为int
24
25 client.sendall("start".encode("utf-8")) #发送字节start
26
27 sum_data = "" #初始汇总的数据
28 while length >= 0: #循环收数据
29 server_data = client.recv(1024)
30 length -=1024
31 sum_data +=server_data.decode("utf-8")
32 print(sum_data) #打印最终的执行数据
33
34 client.close()
35
36 ssh 客户端多用户同时连接
ssh 客户端多用户同时连接
ThreadingTCPServer源码剖析
内部调用流程为:
- 启动服务端程序
- 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
- 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
- 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
- 当客户端连接到达服务器
- 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
- 执行 ThreadingMixIn.process_request_thread 方法
- 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
对源码进行精简做一个程序:
import socket
import threading
import select
def process(request, client_address):
print request,client_address
conn = request
conn.sendall( '欢迎致电 10086,请输入1xxx,0转人工服务.' )
flag = True
while flag:
data = conn.recv( 1024 )
if data = = 'exit' :
flag = False
elif data = = '0' :
conn.sendall( '通过可能会被录音.balabala一大推' )
else :
conn.sendall( '请重新输入.' )
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(( '127.0.0.1' , 8002 ))
sk.listen( 5 )
while True :
r, w, e = select.select([sk,],[],[], 1 )
print 'looping'
if sk in r:
print 'get request'
request, client_address = sk.accept()
t = threading.Thread(target = process, args = (request, client_address))
t.daemon = False
t.start()
sk.close()
如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)