socketserver源码解析和协程版socketserver

  来,贴上一段代码让你仰慕一下欧socketserver的魅力,看欧怎么完美实现多并发的魅力

client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import socket
 
 
ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)
 
while True:
    data = sk.recv(1024)
    print('receive:',data.decode())
    inp = input('please input:')
    sk.sendall(inp.encode())
    if inp == 'exit':
        break
 
sk.close()

 server

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
'''
对于socketserver,你需要做的事:
    定义个一类,继续socketserver.BaseRequestHandler
    重写handle方法
    把写好的类和端口进行实例,启动程序
'''
 
import socketserver
 
class MyServer(socketserver.BaseRequestHandler):
 
    def handle(self):
        # print self.request,self.client_address,self.server
        conn = self.request
        conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.'.encode())
        Flag = True
        while Flag:
            data = conn.recv(1024).decode()
            if data == 'exit':
                Flag = False
            elif data == '0':
                conn.sendall('通过可能会被录音.balabala一大推'.encode())
            else:
                conn.sendall('请重新输入.'.encode())
 
 
if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
    server.serve_forever()

代码执行看源码

   我们看到最后两句代码,第一句--带有括号,我们第一想到的是要么是个函数,要是是个类,那么看一下什么吧

  是个类,继承了ThreadingMixIn和TCPServer两个类,好!实例对象肯定要找构造方法,当前这个类没有,就需要到父类中找了,从左往右

  我们发现ThreadingMixIn类里没有,那肯定在TCPServer类里啦,果真在,并且还执行了TCPServer的父类BaseServer里的构造方法

  在BaseServer里只是做了一些数据初始化的事,那我们回到TCPServer里构造方法里接着往下看吧

  执行父类构造方法后,实例一个socket对象,然后就是在if下执行self.server_bind()方法,要想知道这个方法,必须弄清楚self是指代谁?

  说到这里,我必须屡屡类的继承关系了...

  self是指实例对象,谁实例的,是ThreadingTCPServer类,所以找server_bind方法,要从ThreadingTCPServer往上找

  我们发现这个方法还是TCPServer里,

  似乎是做了绑定socket的事,这里过,在找看构造方法里,后又执行self.server_activate方法,我们按照那继承关系又在TCPServer找到了

 

  似乎是做了有关监听数的事,好这里构造方法执行完毕,第一代码也就这样看完了

  看到第二句,开始找serve_forever方法在哪了

  在BaseServer里,这方法大概说的是,一次处理一个请求直到连接关闭,如果处理其他要求另外开启一个线程

  我发现下有个self._handle_request_noblock(),我们看看这个是做啥的吧?

  还是在BaseServer,好像是做不阻塞的工作

  在这里还是执行了一个process_request方法,这个方法我们在ThreadingMixIn找到了

 

  主要是实例线程和启动线程的...

  没错,似乎我有了一点感悟--

  socketserver是基于多线程来做的,并做到多并发处理

内部调用流程为:

  • 启动服务端程序
  • 执行 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方法)

 

 

服务类:

 

  SocketServer提供了5个基本的服务类:

 

  BaseServer: 基础类,由于下面四个网络服务类的继承

 

  TCPServer:针对TCP套接字流

 

  UDPServer:针对UDP数据报套接字

 

  UnixStreamServer:处理流式套接字,与TCPServer配合

 

  UnixDatagramServer:处理数据报套接字,与UDPServer配合

 

异步处理类:

  这个四个服务类都是同步处理请求的。一个请求没处理完不能处理下一个请求。要想支持异步模型,可以利用多继承让server类继承ForkingMixIn 或 ThreadingMixIn。

  ForkingMixIn: 利用多进程(分叉)实现异步。(Mix-in class to handle each request in a new process)

  ThreadingMixIn: 利用多线程实现异步。(Mix-in class to handle each request in a new thread)

请求处理类:

  要实现一项服务,还必须派生一个handler请求处理类,并重写父类的handle()方法。handle方法就是用来专门是处理请求的。该模块是通过服务类和请求处理类组合来处理请求的。

  SocketServer模块提供的请求处理类有BaseRequestHandler,以及它的派生类StreamRequestHandlerDatagramRequestHandler。从名字看出可以一个处理流式套接字,一个处理数据报套接字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#服务器端
 
from SocketServer import TCPServer,StreamRequestHandler,\
    ThreadingMixIn, ForkingMixIn
 
 
 
#定义请求处理类
class Handler(StreamRequestHandler):
    def handle(self):
        addr = self.request.getpeername()
        print 'connection:', addr
        while 1:
            self.request.sendall(self.request.recv(1024))
 
 
#实例化服务类对象
server = TCPServer(
    server_address=('127.0.0.1', 8123),     # address
    RequestHandlerClass=Handler             # 请求类
)
 
#开启服务
server.serve_forever()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#客户端
 
import socket
 
def socketClient():
    so = socket.socket()
    so.connect(('127.0.0.1', 8123))
    # so.close()
    while 1:
        so.sendall(raw_input('msg'))
        print so.recv(1024)
 
if __name__ == '__main__':
    socketClient()

 多线程服务端

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
from SocketServer import TCPServer,StreamRequestHandler,\
    ThreadingMixIn, ForkingMixIn
 
 
#定义基于多线程的服务类
class Server(ThreadingMixIn, TCPServer):
    pass
 
 
#定义请求处理类
class Handler(StreamRequestHandler):
    def handle(self):
        addr = self.request.getpeername()
        print 'connection:', addr
        while 1:
            self.request.sendall(self.request.recv(1024))
 
 
#实例化服务类对象
server = Server(
    server_address=('127.0.0.1', 8123),     # address
    RequestHandlerClass=Handler             # 请求类
)
 
#开启服务
server.serve_forever()

 源码分析:

 

基于协程实现socket多并发

  在这里,首先我们先了解一下协程一个概念:

  • 协程又称微线程,单线程实现异步并发

  • 线程寄存在cpu里,而协程有自己的寄存器,上下文和栈

  • 由于协程本质上是单线程,所以不存在上下文切换花销,以及锁和同步的概念

  • 低成本,高并发,唯一不足的就是不能利用cpu的核资源

  • 你就记住协程干了这么一件事:遇IO阻塞就去做别的事了(socket连接就是IO操作)

  从上面的源码解析,我们知道,socketserver实现多并发本质就是多线程或多进程,但这样还是有些低效,你想啊,如果有几万客户连接过来,就要创建几万个线程,如果用的人其实不是很多,CPU还要不断的去检测socket客户端有没有传输数据,花销很大,效率就低

server

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 sys
import socket
import time
import gevent
  
from gevent import socket,monkey
monkey.patch_all()
  
  
def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)
  
  
  
def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:
                conn.shutdown(socket.SHUT_WR)
  
    except Exception as  ex:
        print(ex)
    finally:
        conn.close()
if __name__ == '__main__':
    server(8001)

 client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import socket
  
HOST = 'localhost'    # The remote host
PORT = 8001           # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"),encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    #print(data)
  
    print('Received', repr(data))
s.close()

 



如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   财经知识狂魔  阅读(510)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示