Socket Server模块

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

Python之路【第七篇续】:进程、线程、协程
socket server 和 select & epoll 还是不太一样他的本质是:客户端第一次链接的时候,只要一进来,我服务端有个while循环为你创建一个
线程和进程,客户端就和服务端直接创建通信,以后传送数据什么的就不会通过server端了,直接他俩通过线程或者进程通信就可以了!

如果在多进程的时候,client1和client2他们同时传输10G的文件都是互相不影响!
如果在多线程的时候,python中的多线程,在同一时间只有一个线程在工作,他底层会自动进行上下文切换,client1传一点,client2传一点。

知识回顾:
python中的多线程,有一个GIL在同一时间只有一个线程在工作,他底层会自动进行上下文切换.
这样会导致python的多线程效率会很低,也就是人们经常说的python多线程问题
Python之路【第七篇续】:进程、线程、协程

如下图:

第一次连接后,数据通讯就通过线程或进程进行数据交换(红色箭头)

Python之路【第七篇续】:进程、线程、协程

ThreadingTCPServer

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

1、ThreadingTCPServer基础

使用ThreadingTCPServer:

  • 创建一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServer

class MyServer(SocketServer.BaseRequestHandler):

    def handle(self): #定义handle方法
        # print self.request,self.client_address,self.server
        conn = self.request #如果连接请求过来,获取client端对象
        conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.') #发送一个信息
        Flag = True #并把Flag设置为True
        while Flag:当Flag为True的时候执行
            data = conn.recv(1024) #接收client端数据
            if data == 'exit': #判断如果data  == 'exit' 退出
                Flag = False #并把Flag设置为Flase
            elif data == '0': #如果为 == ‘0’ 
                conn.sendall('通过可能会被录音.balabala一大推') #发送数据
            else:#上面的都没匹配上,发送请重新输入
                conn.sendall('请重新输入.') 


if __name__ == '__main__':
    server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer) #实例化对象,设置启动的IP/PORT并把自己定义的类写上作为SocketServer.ThreadingTCPServer的构造函数
    server.serve_forever() #调用对象中的启动方法
Socket Server
#!/usr/bin/env python
# -*- coding:utf-8 -*-

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
    inp = raw_input('please input:')
    sk.sendall(inp)
    if inp == 'exit':
        break

sk.close()
Socket Client

2、ThreadingTCPServer源码剖析

 Python之路【第七篇续】:进程、线程、协程

学会看源码非常重要!不能仅仅光会用!大赞~ 知道他的过程和实现~ 怎么学会看源码呢?多看然后画类图,如上图!!!

在理解的时候可以把他们想象为,把所有需要用的方法,都抓到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方法)

精简源码:

模拟Socekt Server的简化版本:

Python之路【第七篇续】:进程、线程、协程
import socket
import threading
import select


def process(request, client_address): #模拟定义的handle()方法,这个方法内的代码是socket server与Client端交互代码
    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:  #这里一个while循环循环监控sk文件句柄
    r, w, e = select.select([sk,],[],[],1)
    print 'looping'
    if sk in r: #当sk文件句柄发生变化的时候说明是新的客户端连接过来了
        print 'get request'
        request, client_address = sk.accept()
        t = threading.Thread(target=process, args=(request, client_address)) #创建一个线程,并调用自己定义的process方法执行~然后样客户端与之交互
        t.daemon = False
        t.start()

sk.close()
Python之路【第七篇续】:进程、线程、协程

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

ForkingTCPServer

ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程”  和 “进程”。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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转人工服务.')
        Flag = True
        while Flag:
            data = conn.recv(1024)
            if data == 'exit':
                Flag = False
            elif data == '0':
                conn.sendall('通过可能会被录音.balabala一大推')
            else:
                conn.sendall('请重新输入.')


if __name__ == '__main__':
    server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyServer)
    server.serve_forever()
socket server - multiprocess

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-02-23
  • 2021-07-18
  • 2021-09-10
  • 2021-04-28
  • 2021-11-14
猜你喜欢
  • 2022-12-23
  • 2021-05-18
  • 2022-12-23
  • 2022-02-13
  • 2021-12-25
  • 2022-12-23
相关资源
相似解决方案