正如前面的socket模块部分看到的一样,写一个简单套接字服务器不是很难,如果想实现超出继承的应用,最好寻求一些帮助,socketserver模块是标准库中很多服务器框架的基础,这些服务器架构包括BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXMLRPCServer、DocXMLRPCServer,所有的这些服务器框架都为基础服务器增加了特定功能;

  socketserver内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求

harmonyos 多次socket连接 socket 多个连接_运维

 

ThreadingTCPServer(多线程,真并发

ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

使用ThreadingTCPServer:

  • 创建一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer

用socketserver对ssh程序做修改,实现多用户同时操作互不影响




harmonyos 多次socket连接 socket 多个连接_服务端_02

harmonyos 多次socket连接 socket 多个连接_python_03

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 服务端多用户同时连接



harmonyos 多次socket连接 socket 多个连接_服务端_02

harmonyos 多次socket连接 socket 多个连接_python_03

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源码剖析

harmonyos 多次socket连接 socket 多个连接_python_06

内部调用流程为:

  • 启动服务端程序
  • 执行 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个客户端链接(长连接)