[python全栈]06.多线程(5)

目录

  1. 多线程并发
  2. 集成模块的使用
  3. HTTPServer
  4. 协程基础
  5. gevent

1. 多线程并发

threading的多线程并发

对比多进程并发 :
	1. 线程消耗资源较少
	2. 线程应更注意共享资源的操作
	3. 在python中应注意GIL问题,网络延迟较高,线程并发也是一种可行方法

实现步骤:
	1. 创建套接字,绑定监听
	2. 接受客户端请求,创建新的线程
	3. 主线程继续接收,其他客户端连接
	4. 分支线程启动对应的函数处理客户端请求
	5. 当客户端断开,分支线程结束
traceback模块
traceback.print_exc()
功能 : 更详细的打印异常信息
和 try ... except 一起使用
eg:
import traceback
a = 10
try:
	b = a / 0
except Exception as e:
	print("Error: ",e)
print("Program Over")

多线程客户端

#thread_server.py

from socket import *
import os,sys
from threading import *

def handler(connfd):
    print("Connect from :",connfd.getpeername())
    while True:
        data = connfd.recv(1024)
        if not data :
            break
        print(data.decode())
        connfd.send("Receive Request".encode())
    connfd.close()


HOST = "0.0.0.0"
PORT = 8888
ADDR = (HOST,PORT)
print("Lisen Port = ",PORT)
#创建套接字
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(ADDR)
s.listen(5)

#等待客户端请求
while True:
    try:
        connfd,addr = s.accept()
    except KeyboardInterrupt:
        s.close()
        sys.exit("server quit")
    except Exception as e:
        print("Error: ",e)
        continue

    t = Thread(target = handler,args=(connfd,))
    t.setDaemon(True)
    t.start()

2. 集成模块的使用

集成模块的使用 :
python2 SocketServer
python3 socketserver
功能 :通过模块的不同类的组合完成多进程/多线程 的 tcp/udp 的并发
>>>
import socketserver
dir(socketserver)
>>>
StreamRequestHandler  处理tcp套接字请求
DatagramRequestHandler  处理udp套接字请求

TCPServer  创建tcp server
UDPServer  创建udp server

ForkingMixIn   创建多进程
ForkingTCPServer -->  ForkingMinIn + TCPServer
ForkingUDPServer -->  ForkingMinIn + UDPServer

ThreadingMixIn  创建多线程
ThreadingTCPServer --> ThreadingMinIn + TCPServer
ThreadingUDPServer --> ThreadingMinIn + UDPServer

TCP多进程并发

#socketserver.py
#TCP多进程并发
from socketserver import *

#class Server(ForkingMixIn,TCPServer):
#class Server(ForkingTCPServer):
class Server(ThreadingMixIn,TCPServer):
    pass

class Handler(StreamRequestHandler):
    def handler(self):
        #self.request <==> accept()返回的套接字
        print("Connect from:",self.request.getpeername())
        while True:
            data = self.request.recv(1024)
            if not data:
                break
            print(data.decode())
            self.request.send(b"Receive")

if __name__ == "__main__":
    server_addr = ("0.0.0.0",8888)
    print("listen to the port 8888...")

    #创建服务器对象
    server = Server(server_addr,Handler)
    #启动服务器
    server.serve_forever()

UDP多线程并发

  pass

class Handler(DatagramRequestHandler):
    def handler(self):
        while True:
            data,addr = self.rfile.readline()
            print(self.client_address)
            if not data:
                break
            print(data.decode())
            self.wfile.write(b"Receive")

if __name__ == "__main__":
    server_addr = ("0.0.0.0",8888)
    print("listen to the port 8888...")

    #创建服务器对象
    server = Server(server_addr,Handler)
    #启动服务器
    server.serve_forever()

3. HTTPServer

HTTPServer 2.0
1.接收客户端请求
2.解析客户端请求
3.组织数据,形成 HTTP response
4.将数据发送给客户端
升级 :
1.采用多线程并发接收多个客户端请求
2.基本的请求解析,根据请求返回相应的内容
3.除了可以请求静态网页,也可以请求简单的数据
4.将所有功能封装在一个类中 
技术点 :
1. socket TCP 套接字
2.http协议的请求响应格式
3.线程并发的创建方法
4.类的基本使用 

HTTP Server 2.0

#coding = utf-8
'''
HTTP Server Ver.2.0
update log
1.多线程并发
2.可以请求简单数据
3.能进行简单请求解析
4.结构使用类进行封装
'''
from socket import *
from threading import Thread
import sys

#httpserver类 封装具体的服务器功能
class HTTPServer(object):
    def __init__(self,server_addr,static_dir):
        self.server_addr = server_addr
        self.static_dir = static_dir
        self.ip = server_addr[0]
        self.port = server_addr[1]
        #创建套接字
        self.create_socket()

    def create_socket(self):
        self.sockfd = socket()
        self.sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
        self.sockfd.bind(self.server_addr)

    #设置监听等待客户端连接
    def server_forever(self):
        self.sockfd.listen(5)
        print("Listen the port",self.port)
        while True:
            try:
                connfd,addr = self.sockfd.accept()
            except KeyboardInterrupt:
                self.sockfd.close()
                sys.exit("Server Quit")
            except Exception as e:
                print("Error: ",e)
                continue
            #创建新的线程处理请求
            clientThread = Thread(target = self.handleRequest,args=(connfd,))
            clientThread.setDaemon(True)
            clientThread.start()
    #具体的客户端请求函数
    def handleRequest(self,connfd):
        #接受客户端请求
        request = connfd.recv(4096)
        #解析请求内容
        requestHeaders = request.splitlines()
        print(connfd.getpeername(),":",requestHeaders[0])
        #获取具体的请求内容
        getRequset = str(requestHeaders[0]).split(" ")[1]

        if getRequset == "/" or getRequset[-5:] == ".html":
            self.get_html(connfd,getRequset)
        else:
            self.get_data(connfd,getRequset)
        connfd.close()

    def get_html(self,connfd,getRequset):
        if getRequset == "/":
            filename = self.static_dir+"/index.html"
        else:
            filename = self.static_dir+getRequset
        try:
            f = open(filename)
        except Exception:
            #没有找到网页,返回404
            responseHeaders = "HTTP/1.1 404 NOT FOUND\r\n"
            responseHeaders += "\r\n"
            responseBody = "Sorry,not found the page"
        else:
            responseHeaders = "HTTP/1.1 200 OK\r\n"
            responseHeaders += "\r\n"
            responseBody = f.read()
        finally:
            response = responseHeaders + responseBody
            connfd.send(response.encode())           

    def get_data(self,connfd,getRequset):
        urls = ['/time','/edu','/python']
        if getRequset in urls:
            responseHeaders = "HTTP/1.1 200 OK\r\n"
            responseHeaders += "\r\n"
            if getRequset == '/time':
                import time
                responseBody = time.ctime()
            elif getRequset == '/edu':
                responseBody = "Let's study"
            elif getRequset == '/python':
                responseBody = "Life is short,I use Python"                            
        else:
            responseHeaders = "HTTP/1.1 404 NOT FOUNF\r\n"
            responseHeaders += "\r\n"
            responseBody = "Sorry,not found the data"  
            
        response = responseHeaders + responseBody
        connfd.send(response.encode())                      

if __name__ == "__main__":
    #服务器IP
    server_addr = ("0.0.0.0",8000)
    #我的静态页面存储目录
    static_dir = "./static"

    #生成对象
    httpd = HTTPServer(server_addr,static_dir)
    #启动服务器
    httpd.server_forever()

4. 协程基础

协程 (高并发技术):
定义:协程的本质是一个单线程程序,所以协程不能使用计算机多核资源
		又称纤程,微线程
作用:能够高效的完成并发任务,占用较少的资源
		因此协程的并发量较高
原理:通过记录应用层的上下文栈区,实现在运行中进行上下文跳转
		达到可以选择性地运行想要运行的部分
		以此提高程序的运行效率
优点 : 消耗资源少 / 无需切换开销
		无须同步互斥  / IO并发性好
缺点 : 无法利用计算机多核

yield ---> 协程实现的基本关键字
第三方库 :greenlet / gevent

greenlet:
greenlet.greenlet()	生成协程对象
gr.swith()	选择要执行的协程事件
from greenlet import greenlet

def test1():
    print(12)
    gr2.switch()
    print(34)
    gr2.switch()

def test2():
    print(56)
    gr1.switch()
    print(78)

#生成协程对象
gr1 = greenlet(test1)
gr2 = greenlet(test2)

gr1.switch()

#>>>12 56 34 78

5. gevent

1. 将协程事件封装为函数
2. 生成协程对象	
	gevent.spawn(func,argv)
	功能 : 生成协程对象
	参数 : func 协程函数 / argv 给协程函数传参
	返回值 : 协程对象
3.回收协程
	gevent.joinall()
	功能: 回收协程
	参数: 列表:将要回收的协程对象放入列表

gevent.sleep(n)
功能:设置协程阻塞,让协程跳转
参数: n 阻塞时间
 from gevent import monkey
 monkey.patch_all()
 功能 : 修改套接字的IO阻塞行为
 *必须在socket导入之前使用
#gevent0.py
import gevent
from time import sleep

def foo(a,b):
    print("a = %d, b = %d"%(a,b))
    gevent.sleep(2)
    print("Running foo again")

def bar():
    print("Running int bar")
    gevent.sleep(3)
    print("Running bar again")

#生成协程
f = gevent.spawn(foo,1,2)
g = gevent.spawn(bar)
#sleep(3)
print("======")
gevent.joinall([f,g])	#特定阻塞
print("++++++")
======
a = 1, b = 2
Running int bar
Running foo again
Running bar again
++++++

实现套接字的协程并发

import gevent

from gevent import monkey
monkey.patch_all()

from socket import *
from time import ctime

def server(port):
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(("0.0.0.0",port))
    s.listen(3)
    while True:
        c,addr = s.accept()
        print("connect from",addr)
        #循环服务器
        #handler(c)
        #协程服务器
        gevent.spawn(handler,c)


def handler(c):
    while True:
        data = c.recv(1024)
        if not data:
            break
        print(data.decode())
        c.send(ctime().encode())
    c.close()

if __name__ == "__main__":
    server(8888)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值