IO多路复用

C:Python27python.exe D:/weixin/temp/yield_tmp.py
______________1
______________2
______________3
______________4
______________5
______________6
______________7
______________8
______________9
______________10
______________1
______________1
______________2
______________2
______________3
______________3
______________4
______________4
______________5
______________5
______________6
______________6
______________7
______________7
______________8
______________8
______________9
______________9
______________10
______________10

Process finished with exit code 0

五、协程

协程:是单线程下的现身,又称微线程、纤程,罗马尼亚语名:Coroutine协程是生机勃勃种顾客态的轻量级线程,协程是由客户程序本身调控调治的。

亟待重申的是:

1.
python的线程属于基本级其他,即由操作系统调控调整(如单线程风姿浪漫旦遭受io就被迫交出cpu推行权限,切换其余线程运维)

  1. 单线程内张开协程,大器晚成旦遇见io,从应用程序等第(而非操作系统)调节切换

相对来说操作系统调节线程的切换,客商在单线程内决定协程的切换,优点如下:

1.
协程的切换费用越来越小,属于程序等第的切换,操作系统完全感知不到,由此特别轻量级

  1. 单线程内就能够兑现产出的功力,最大限度地使用cpu。

要促成协程,关键在于客商程序自身支配程序切换,切换此前必须由客户程序自身保留协程上叁次调用时的景况,如此,每一趟重复调用时,能够从上次的岗位继续实施

(详细的:协程具备协和的寄放器上下文和栈。协程调治切换时,将寄放器上下文和栈保存到此外地方,在切回到的时候,恢复生机原先保存的寄放器上下文和栈)

     
这里未有选取yield协程,这么些python自带的并非很圆满,至于缘何有待于你去钻探了。

七、socketserver达成产出

依附TCP的套接字,关键正是多个循环,二个连连循环,贰个通讯循环。

SocketServer内部运用 IO多路复用 以至 “十六线程” 和 “多进度”
,进而完毕产出管理多个客商端供给的Socket服务端。即:每一个客商端供给连接到服务器时,Socket服务端都会在服务器是创制二个“线程”也许“进度”
专责处理当下客商端的全数诉求。

socketserver模块中的类分为两大类:server类(解决链接难点)和request类(解决通信难题)

server类:

永利澳门游戏网站 1

server类

request类:

永利澳门游戏网站 2

request类

线程server类的持续关系:

永利澳门游戏网站 3

线程server类的继续关系

经过server类的三番两回关系:

永利澳门游戏网站 4

经过server类的后续关系

request类的接轨关系:

永利澳门游戏网站 5

request类的存在延续关系

以下述代码为例,解析socketserver源码:

ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
ftpserver.serve_forever()

寻觅属性的各类:ThreadingTCPServer –> ThreadingMixIn –>
TCPServer->BaseServer

  1. 实例化获得ftpserver,先找类ThreadingTCPServer__init__,在TCPServer中找到,进而施行server_bind,server_active
  2. ftpserver下的serve_forever,在BaseServer中找到,进而施行self._handle_request_noblock(),该办法相近是在BaseServer
  3. 执行self._handle_request_noblock()任何时候试行request, client_address = self.get_request()(就是TCPServer中的self.socket.accept()),然后施行self.process_request(request, client_address)
  4. ThreadingMixIn中找到process_request,开启八线程应对现身,进而实践process_request_thread,执行self.finish_request(request, client_address)
  5. 上述四部分成功了链接循环,本有的开首步入拍卖通信部分,在BaseServer中找到finish_request,触发大家生死相许定义的类的实例化,去找__init__艺术,而作者辈团结定义的类没有该方法,则去它的父类也等于BaseRequestHandler中找….

源码剖析总括:
依附tcp的socketserver大家和好定义的类中的

  • self.server 即套接字对象
  • self.request 即贰个链接
  • self.client_address 即顾客端地址

基于udp的socketserver大家自身定义的类中的

  • self.request是三个元组(第多个因素是客商端发来的数量,第二有个别是服务端的udp套接字对象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
  • self.client_address即顾客端地址。

 

七、基于UDP的套接字

  • recvfrom(buffersize[, flags])接到消息,buffersize是三回收到多少个字节的多寡。
  • sendto(data[, flags], address)
    发送音讯,data是要发送的二进制数据,address是要发送的地方,元组方式,包括IP和端口

服务端:

from socket import *
s=socket(AF_INET,SOCK_DGRAM)  #创建一个基于UDP的服务端套接字,注意使用SOCK_DGRAM类型
s.bind(('127.0.0.1',8080))  #绑定地址和端口,元组形式

while True:    #通信循环
    client_msg,client_addr=s.recvfrom(1024) #接收消息
    print(client_msg)
    s.sendto(client_msg.upper(),client_addr) #发送消息

客户端:

from socket import *
c=socket(AF_INET,SOCK_DGRAM)   #创建客户端套接字

while True:
    msg=input('>>: ').strip()
    c.sendto(msg.encode('utf-8'),('127.0.0.1',8080)) #发送消息
    server_msg,server_addr=c.recvfrom(1024) #接收消息
    print('from server:%s msg:%s' %(server_addr,server_msg))

效仿即时聊天
出于UDP无连接,所以能够並且四个客商端去跟服务端通讯

服务端:

from socket import *

server_address = ("127.0.0.1",60000)
udp_server_sock = socket(AF_INET,SOCK_DGRAM)
udp_server_sock.bind(server_address)

while True:
    qq_msg,addr = udp_server_sock.recvfrom(1024)
    print("来自[%s:%s]的一条消息:33[32m%s33[0m"%(addr[0],addr[1],qq_msg.decode("utf-8")))
    back_msg = input("回复消息:").strip()
    udp_server_sock.sendto(back_msg.encode("utf-8"),addr)

udp_server_sock.close()

客户端:

from socket import *

BUFSIZE = 1024
udp_client_sock = socket(AF_INET,SOCK_DGRAM)
qq_name_dic = {
    "alex":("127.0.0.1",60000),
    "egon":("127.0.0.1",60000),
    "seven":("127.0.0.1",60000),
    "yuan":("127.0.0.1",60000),
}

while True:
    qq_name = input("请选择聊天对象:").strip()
    while True:
        msg = input("请输入消息,回车发送:").strip()
        if msg == "quit":break
        if not msg or not qq_name or qq_name not in qq_name_dic:continue
        print(msg,qq_name_dic[qq_name])
        udp_client_sock.sendto(msg.encode("utf-8"),qq_name_dic[qq_name])

        back_msg,addr = udp_client_sock.recvfrom(BUFSIZE)
        print("来自[%s:%s]的一条消息:33[32m%s33[0m" %(addr[0],addr[1],back_msg.decode("utf-8")))
udp_client_sock.close()

注意:
1.你独自运营方面包车型大巴udp的客商端,你意识并不会报错,相反tcp却会报错,因为udp左券只负责把包发出去,对方收不收,作者根本不管,而tcp是依据链接的,必得有二个服务端先运转着,客商端去跟服务端创设链接然后依托于链接技术传递音讯,任何一方试图把链接摧毁都会产生对方程序的垮台。

2.地方的udp程序,你注释任何一条客商端的sendinto,服务端都会堵塞,为何?因为服务端有多少个recvfrom将要对应几个sendinto,哪怕是sendinto(b”)那也要有。

3.recvfrom(buffersize)要是设置每趟接收数据的字节数,小于对方发送的数额字节数,假如运转Linux遭逢下,则只会收取到recvfrom()所设置的字节数的数量;而意气风发旦运营windows遭遇下,则会报错。

基于socketserver金玉满堂三十二线程的UDP服务端:

import socketserver

class MyUDPhandler(socketserver.BaseRequestHandler):
    def handle(self):
        client_msg,s=self.request
        s.sendto(client_msg.upper(),self.client_address)

if __name__ == '__main__':
    s=socketserver.ThreadingUDPServer(('127.0.0.1',60000),MyUDPhandler)
    s.serve_forever()

      

1.3.4 线程的join与setDaemon

与经过的法子都是看似的,其实multiprocessing模块是模仿threading模块的接口;

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.setDaemon(True) #设置为守护线程,主线程结束,子线程也跟着线束。
    t.start()
    t.join()  #主线程等待子线程运行结束
    print('主线程')
    print(t.is_alive())

      这里运用相比较完备的第三方协程包gevent

6.1 ThreadingTCPServer

ThreadingTCPServer达成的Soket服务器内部会为种种client创造一个“线程”,该线程用来和顾客端进行互动。

使用ThreadingTCPServer:

  • 创造三个持续自 SocketServer.BaseRequestHandler 的类
  • 类中必得定义一个名称叫 handle 的不二等秘书籍
  • 启动ThreadingTCPServer。
  • 启动serve_forever() 链接循环

服务端:

import socketserver

class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        # print(addr)
        conn.sendall("欢迎致电10086,请输入1XXX,0转人工服务。".encode("utf-8"))
        Flag = True
        while Flag:
            data = conn.recv(1024).decode("utf-8")
            if data == "exit":
                Flag = False
            elif data == '0':
                conn.sendall("您的通话可能会被录音。。。".encode("utf-8"))
            else:
                conn.sendall("请重新输入。".encode('utf-8'))

if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(("127.0.0.1",60000),MyServer)
    server.serve_forever()  #内部实现while循环监听是否有客户端请求到达。

客户端:

import socket

ip_port = ('127.0.0.1',60000)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)

while True:
    data = sk.recv(1024).decode("utf-8")
    print('receive:',data)
    inp = input('please input:')
    sk.sendall(inp.encode('utf-8'))
    if inp == 'exit':
        break
sk.close()

      那么python的八线程就从未怎么用了吧?

2.1 什么是大局解释器锁GIL

Python代码的推行由Python
虚构机(也叫解释器主循环,CPython版本)来决定,Python
在设计之初就记挂到要在解释器的主循环中,同期唯有多少个线程在试行,即在随机时刻,唯有贰个线程在解释器中运行。对Python
虚拟机的寻访由全局解释器锁(GIL)来调整,就是这些锁能保障同不日常刻独有贰个线程在运作。
在四线程情形中,Python 虚拟机按以下办法施行:

  1. 设置GIL
  2. 切换成三个线程去运作
  3. 运行:
    a. 钦点数量的字节码指令,只怕
    b. 线程主动让出调节(能够调用time.sleep(0))
  4. 把线程设置为睡眠境况
  5. 解锁GIL
  6. 重复重复以上全数手续

在调用外界代码(如C/C++扩充函数)的时候,GIL
将会被锁定,直到那一个函数停止停止(由于在这里之间从未Python
的字节码被运维,所以不会做线程切换)。

实施结果:开了多个进度,各类进程下实践10个体协会程合营任务

1.1 直接运用利用threading.Thread()类实例化

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.start()

    print('主线程')

各样进程下N个体协会程,   

5.3 gevent完结协程

gevent是二个第三方库,可以轻便通过gevent完结产出同步或异步编制程序,在gevent中用到的根本是Greenlet,它是以C扩张模块格局接入Python的轻量级协程。greenlet一切周转在主程操作系统进度的在那之中,但它们被合作式地调节和测量检验。相遇I/O阻塞时会自动切换职务。

注意:gevent有本人的I/O阻塞,如:gevent.sleep()和gevent.socket();但是gevent不可能向来识别除自身之外的I/O阻塞,如:time.sleep(2),socket等,要想识别这一个I/O阻塞,必得打二个补丁:from gevent import monkey;monkey.patch_all()

  • 亟需先安装gevent模块
    pip install gevent

  • 开创多个体协会程对象g1
    g1 =gevent.spawn()
    spawn括号内率先个参数是函数名,如eat,前面能够有八个参数,能够是岗位实参或注重字实参,都是传给第三个参数(函数)eat的。

from gevent import monkey;monkey.patch_all()
import gevent

def eat():
    print("点菜。。。")
    gevent.sleep(3)   #等待上菜
    print("吃菜。。。")

def play():
    print("玩手机。。。")
    gevent.sleep(5)  #网卡了
    print("看NBA...")

# gevent.spawn(eat)
# gevent.spawn(play)
# print('主') # 直接结束

#因而也需要join方法,进程或现场的jion方法只能join一个,而gevent的joinall方法可以join多个
g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])  #传一个gevent对象列表。
print("主线程")

"""
输出结果:
点菜。。。
玩手机。。。    
##等待大概3秒       此行没打印
吃菜。。。
##等待大概2秒          此行没打印
看NBA...
主线程
"""

注:永利澳门游戏网站,上例中的gevent.sleep(3)是模拟的I/O阻塞。跟time.sleep(3)成效相像。

同步/异步

import gevent
def task(pid):
    """
    Some non-deterministic task
    """
    gevent.sleep(0.5)
    print('Task %s done' % pid)

def synchronous():  #同步执行
    for i in range(1, 10):
        task(i)

def asynchronous(): #异步执行
    threads = [gevent.spawn(task, i) for i in range(10)]
    gevent.joinall(threads)

print('Synchronous:')
synchronous()   #执行后,会顺序打印结果

print('Asynchronous:')
asynchronous()  #执行后,会异步同时打印结果,无序的。

爬虫应用

#协程的爬虫应用

from gevent import monkey;monkey.patch_all()
import gevent
import time
import requests

def get_page(url):
    print("GET: %s"%url)
    res = requests.get(url)
    if res.status_code == 200:
        print("%d bytes received from %s"%(len(res.text),url))

start_time = time.time()
g1 = gevent.spawn(get_page,"https://www.python.org")
g2 = gevent.spawn(get_page,"https://www.yahoo.com")
g3 = gevent.spawn(get_page,"https://www.github.com")
gevent.joinall([g1,g2,g3])
stop_time = time.time()
print("run time is %s"%(stop_time-start_time))

上以代码输出结果:

GET: https://www.python.org
GET: https://www.yahoo.com
GET: https://www.github.com
47714 bytes received from https://www.python.org
472773 bytes received from https://www.yahoo.com
98677 bytes received from https://www.github.com
run time is 2.501142978668213

应用:
因而gevent达成单线程下的socket并发,注意:from gevent import monkey;monkey.patch_all()分明要放置导入socket模块在此之前,不然gevent不能够辨认socket的围堵。

服务端代码:

from gevent import monkey;monkey.patch_all()
import gevent
from socket import *

class server:
    def __init__(self,ip,port):
        self.ip = ip
        self.port = port


    def conn_cycle(self):   #连接循环
        tcpsock = socket(AF_INET,SOCK_STREAM)
        tcpsock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
        tcpsock.bind((self.ip,self.port))
        tcpsock.listen(5)
        while True:
            conn,addr = tcpsock.accept()
            gevent.spawn(self.comm_cycle,conn,addr)

    def comm_cycle(self,conn,addr):   #通信循环
        try:
            while True:
                data = conn.recv(1024)
                if not data:break
                print(addr)
                print(data.decode("utf-8"))
                conn.send(data.upper())
        except Exception as e:
            print(e)
        finally:
            conn.close()

s1 = server("127.0.0.1",60000)
print(s1)
s1.conn_cycle()

顾客端代码 :

from socket import *

tcpsock = socket(AF_INET,SOCK_STREAM)
tcpsock.connect(("127.0.0.1",60000))

while True:
    msg = input(">>: ").strip()
    if not msg:continue
    tcpsock.send(msg.encode("utf-8"))
    data = tcpsock.recv(1024)
    print(data.decode("utf-8"))

通过gevent完成产出八个socket客商端去老是服务端

from gevent import monkey;monkey.patch_all()
import gevent
from socket import *

def client(server_ip,port):
    try:
        c = socket(AF_INET,SOCK_STREAM)
        c.connect((server_ip,port))
        count = 0
        while True:
            c.send(("say hello %s"%count).encode("utf-8"))
            msg = c.recv(1024)
            print(msg.decode("utf-8"))
            count+=1
    except Exception as e:
        print(e)
    finally:
        c.close()

# g_l = []
# for i in range(500):
#     g = gevent.spawn(client,'127.0.0.1',60000)
#     g_l.append(g)
# gevent.joinall(g_l)

#上面注释代码可简写为下面代码这样。

threads = [gevent.spawn(client,"127.0.0.1",60000) for i in range(500)]
gevent.joinall(threads)

     
实际上,python在履行八线程的时候,是经过GIL锁,实行上下文切换线程试行,每一遍真实只有三个线程在运作。所以上边才说,未有当真落到实处多现程。

1.3 在叁个进度下展开多少个线程与在八个经过下展开多少个子进度的界别

     
协程有啥收益呢,协程只在单线程中试行,不必要cpu实行上下文切换,协程自动完结子程序切换。

1.3.5 线程相关的此外事办公室法补充

Thread实例对象的秘技:

  • isAlive():再次回到纯种是或不是是活跃的;
  • getName():重回线程名;
  • setName():设置线程名。

threading模块提供的有个别格局:

  • threading.currentThread():重临当前的线程变量
  • threading.enumerate():重临三个含有正在周转的线程的列表。正在运行指线程运行后、结束前,不饱含运营前和苏息后。
  • threading.activeCount():重回正在运营的线程数量,与len(threading.enumerate())有大器晚成致结果。

from threading import Thread
import threading
import os

def work():
    import time
    time.sleep(3)
    print(threading.current_thread().getName())


if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()

    print(threading.current_thread().getName()) #获取当前线程名
    print(threading.current_thread()) #主线程
    print(threading.enumerate()) #连同主线程在内有两个运行的线程,返回的是活跃的线程列表
    print(threading.active_count())  #活跃的线程个数
    print('主线程/主进程')

    '''
    打印结果:
    MainThread
    <_MainThread(MainThread, started 140735268892672)>
    [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
    2
    主线程/主进程
    Thread-1
    '''

      协程,又称微线程,纤程。意大利共和国语名Coroutine。

目录

一、开启线程的两种方式
    1.1 直接利用利用threading.Thread()类实例化
    1.2 创建一个类,并继承Thread类
    1.3 在一个进程下开启多个线程与在一个进程下开启多个子进程的区别
        1.3.1 谁的开启速度更快?
        1.3.2 看看PID的不同
        1.3.3 练习
        1.3.4 线程的join与setDaemon
        1.3.5 线程相关的其他方法补充

二、 Python GIL
    2.1 什么是全局解释器锁GIL
    2.2 全局解释器锁GIL设计理念与限制

三、 Python多进程与多线程对比
四、锁
    4.1 同步锁
    GIL vs Lock
    4.2 死锁与递归锁
    4.3 信号量Semaphore
    4.4 事件Event
    4.5 定时器timer
    4.6 线程队列queue

五、协程
    5.1 yield实现协程
    5.2 greenlet实现协程
    5.3 gevent实现协程

六、IO多路复用

七、socketserver实现并发
    7.1 ThreadingTCPServer

八、基于UDP的套接字

    我们当先五成的时候使用四线程,以致多进程,不过python中出于GIL全局解释器锁的通首至尾的经过,python的八线程并从未真的落实

4.1 同步锁

急需:对八个全局变量,开启玖拾捌个线程,各种线程都对该全局变量做减1操作;

不加锁,代码如下:

import time
import threading

num = 100  #设定一个共享变量
def addNum():
    global num #在每个线程中都获取这个全局变量
    #num-=1

    temp=num
    time.sleep(0.1)
    num =temp-1  # 对此公共变量进行-1操作

thread_list = []

for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('Result: ', num)

分析:以上程序开启100线程并不能够把全局变量num减为0,第三个线程施行addNum遭受I/O阻塞后高速切换来下三个线程推行addNum,由于CPU施行切换的进度相当慢,在0.1秒内就切换完毕了,那就招致了第一个线程在得到num变量后,在time.sleep(0.1)时,其余的线程也都得到了num变量,全数线程获得的num值都以100,所以最后减1操作后,便是99。加锁实现。

加锁,代码如下:

import time
import threading

num = 100   #设定一个共享变量
def addNum():
    with lock:
        global num
        temp = num
        time.sleep(0.1)
        num = temp-1    #对此公共变量进行-1操作

thread_list = []

if __name__ == '__main__':
    lock = threading.Lock()   #由于同一个进程内的线程共享此进程的资源,所以不需要给每个线程传这把锁就可以直接用。
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)

    for t in thread_list:  #等待所有线程执行完毕
        t.join()

    print("result: ",num)

加锁后,第一个线程获得锁后开头操作,第二个线程必须等待第三个线程操作达成后将锁释放后,再与其余线程竞争锁,得到锁的线程才有权操作。那样就保持了数额的张家界,可是拖慢了实施进度。
注意:with locklock.acquire()(加锁)与lock.release()(释放锁)的简写。

import threading

R=threading.Lock()

R.acquire()
'''
对公共数据的操作
'''
R.release()

             
不是以此样子的,python多线程平日用来IO密集型的顺序,那么哪些叫做IO密集型呢,举个例证,举个例子说带有阻塞的。当前线程阻塞等待别的线程实行。

GIL vs Lock

机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock? 

先是大家供给高达共鸣:锁的目标是为了保证分享的数码,同时只可以有一个线程来改进分享的多少

然后,我们得以得出结论:保养分裂的数额就应该加不一致的锁。

末段,难点就很爽朗了,GIL
与Lock是两把锁,爱惜的数目不平等,前面二个是解释器级其他(当然维护的正是解释器品级的多少,譬如垃圾回笼的多寡),前者是维护客户自身花费的应用程序的数量,很显明GIL不担任那事,只好客商自定义加乌鱼理,即Lock

详细的:

因为Python解释器帮你活动准期开展内部存款和储蓄器回笼,你能够了然为python解释器里有二个独自的线程,每过风流罗曼蒂克段时间它起wake
up做二次全局轮询看看哪些内部存款和储蓄器数据是能够被清空的,那时候您自个儿的先后里的线程和
py解释器本人的线程是并发运营的,借使你的线程删除了多个变量,py解释器的污物回笼线程在清空那几个变量的进度中的clearing时刻,恐怕二个任何线程正好又重新给那一个尚未来及得清空的内部存款和储蓄器空间赋值了,结果就有十分大可能率新赋值的数额被删去了,为了消除相符的标题,python解释器简单阴毒的加了锁,即当三个线程运营时,此外人都不可能动,那样就消除了上述的难题,
这足以说是Python开始时代版本的遗留难点。

#coding=utf-8
from multiprocessing import Process
import gevent
#from gevent import monkey; monkey.patch_socket()
#用于协程的了程序
def yield_execFunc(x):
    print('______________%s'%x)


#yield_clist决定协程的数量
#开始协程操作
def yield_start(yield_clist):
    task=[] #用来存储协程
    for i in yield_clist:
        task.append(gevent.spawn(yield_execFunc,i))

    gevent.joinall(task) #执行协程

if  __name__=="__main__":
    list1=[1,2,3,4,5,6,7,8,9,10] #元素个数决定开起的协程数量
    list2=[1,2,3,4,5,6,7,8,9,10]
    list3=[1,2,3,4,5,6,7,8,9,10]
    process_list =[list1,list2,list3] #元素个数决定进程数量
    for plist in process_list:
        p = Process(target=yield_start,args=(plist,))
        p.start()

改良版如下

募集分外并将接纳数据和发送数据分开管理
示例二:

# 示例二
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author : Cai Guangyin

from socket import *
import select

sk1 = socket(AF_INET,SOCK_STREAM)
sk1.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sk1.bind(("127.0.0.1",60000))
sk1.listen(5)

sk2 = socket(AF_INET,SOCK_STREAM)
sk2.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sk2.bind(("127.0.0.1",60001))
sk2.listen(5)


inputs = [sk1,sk2]
w_inputs = []

while True:
    r,w,e = select.select(inputs,w_inputs,inputs,0.1)
    for obj in r:
        if obj in [sk1,sk2]:
            print("新连接:",obj.getsockname())
            conn,addr = obj.accept()
            inputs.append(conn)

        else:
            try:
                # 如果客户端断开连接,将获取异常,并将收取数据data置为空
                data = obj.recv(1024).decode('utf-8')
                print(data)
            except Exception as e:
                data = ""

            if data:
                # 如果obj能正常接收数据,则认为它是一个可写的对象,然后将它加入w_inputs列表
                w_inputs.append(obj)
            else:
                # 如果数据data为空,则从inputs列表中移除此连接对象obj
                print("空消息")
                obj.close()
                inputs.remove(obj)


        print("分割线".center(60,"-"))

    # 遍历可写的对象列表,
    for obj in w:
        obj.send(b'ok')
        # 发送数据后删除w_inputs中的此obj对象,否则客户端断开连接时,会抛出”ConnectionResetError“异常
        w_inputs.remove(obj)

      那么哪些是协程呢?

1.3.1 哪个人的拉开速度越来越快?

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello')

if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()
    print('主线程/主进程')
    '''
    打印结果:
    hello
    主线程/主进程
    '''

    #在主进程下开启子进程
    t=Process(target=work)
    t.start()
    print('主线程/主进程')
    '''
    打印结果:
    主线程/主进程
    hello
    '''

结论:鉴于创设子进度是将主进度完全拷贝生龙活虎份,而线程无需,所以线程的开创速度越来越快。

       现在有诸有此类豆蔻年华项职分:必要从200W个url中获取数据?

经过IO多路复用完成同期监听多少个端口的服务端

示例一:

# 示例一:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author : Cai Guangyin

from socket import socket
import select

sock_1 = socket()
sock_1.bind(("127.0.0.1",60000))
sock_1.listen(5)

sock_2 = socket()
sock_2.bind(("127.0.0.1",60001))
sock_2.listen(5)

inputs = [sock_1,sock_2]

while True:
    # IO多路复用
    # -- select方法,内部进行循环操作,哪个socket对象有变化(连接),就赋值给r;监听socket文件句柄有个数限制(1024个)
    # -- poll方法,也是内部进行循环操作,没有监听个数限制
    # -- epoll方法,通过异步回调,哪个socket文件句柄有变化,就会自动告诉epoll,它有变化,然后将它赋值给r;
    # windows下没有epoll方法,只有Unix下有,windows下只有select方法
    r,w,e=select.select(inputs,[],[],0.2)  #0.2是超时时间
        #当有人连接sock_1时,返回的r,就是[sock_1,];是个列表
        #当有人连接sock_2时,返回的r,就是[sock_2,];是个列表
        #当有多人同时连接sock_1和sock_2时,返回的r,就是[sock_1,sock_2,];是个列表
        #0.2是超时时间,如果这段时间内没有连接进来,那么r就等于一个空列表;
    for obj in r:
        if obj in [sock_1,sock_2]:

            conn, addr = obj.accept()
            inputs.append(conn)
            print("新连接来了:",obj)

        else:
            print("有连接用户发送消息来了:",obj)
            data = obj.recv(1024)
            if not data:break
            obj.sendall(data)

客户端:

# -*- coding:utf-8 -*-
#!/usr/bin/python
# Author : Cai Guangyin

from socket import *

tcpsock = socket(AF_INET,SOCK_STREAM)   #创建一个tcp套接字
tcpsock.connect(("127.0.0.1",60001))     #根据地址连接服务器

while True:   #客户端通信循环
    msg = input(">>: ").strip()   #输入消息
    if not msg:continue           #判断输入是否为空
        #如果客户端发空,会卡住,加此判断,限制用户不能发空
    if msg == 'exit':break       #退出
    tcpsock.send(msg.encode("utf-8"))   #socket只能发送二进制数据
    data = tcpsock.recv(1024)    #接收消息
    print(data.decode("utf-8"))

tcpsock.close()

如上服务端运营时,借使有顾客端断开连接则会抛出如下万分:

永利澳门游戏网站 6

异常

发表评论

电子邮件地址不会被公开。 必填项已用*标注