socketserver(多连接)

  正如前面的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方法)

对源码进行精简做一个程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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()
        = threading.Thread(target=process, args=(request, client_address))
        t.daemon = False
        t.start()
 
sk.close()

如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)

 

 

 

posted @ 2017-04-20 16:37  李小六儿  阅读(4628)  评论(0编辑  收藏  举报