Python学习第十五周总结

网络编程

一、网络编程

1、Socket介绍:

Python中提供socket.py标准库,非常底层的接口库。

Socket是一种通用的网络编程接口,和网络层次没有一一对应关系。

 

跨操作系统的。紧密结合tcp和udp来使用的。

 

接口简单,但是背后的原理不简单,ip加tcp,通过插两端。通过socket通道;连接程序。

建立关联。

 

 

apc库。

加端口是因为应用程序太多了。绑定ip地址,作为假端口。

 

端口是由谁管理的

一般都是tcp和udp编程。Socket基本的,chatserver。

 

 

协议族:AF 表示address family ,用于socket()的第一个参数。

名称 含义
AF_INET Ipv4
AF_INET6 Ipv6
AF_UNIX Unix domain socket,windows没有

第三个本机使用效率不错的,通用的话就是应该进一步考虑了。

Socket类型:

名称 含义
Sock_STREAM 面向连接的流套接字,默认值,tcp协议
SOCK_DGRAM 无连接的数据报文套接字,UDP协议

 

 

2、tcp编程

 

Tcp编程是IO密集型。多线程处理的问题。

 

Server端:1、要有socket接口。2、找ip地址和端口绑定。3、监听端口。4、accept,接受socket,创建小的socket端。直接和应用程序连接在一起的。5、读取用户数据。6、写,发送数据。7、数据完成后断开。

Client端:1、要有socket端,主动连接别人。2、connect建立连接,有socket,和端口和ip。

  • 写,发送数据。4、读取服务器端数据。5、数据完成后关闭了。

服务器端没有响应了,tcp协议管理。

 

Socket会占用描述符,每一个都会创建一个文件描述符。客户端看到只有的是一个。

 

Import socket

Server = socket.socket()   socket接口。

Server.bind(ipaddr)

Server.bind((‘0.0.0.0’,9999))绑定

Server.listen()监听

 

 

S2,iP2 = server.accept()

 

S1.recv(1024)缓冲区大小。

 

S1.send(b’ack’)

 

 

 

decode()解码

encode()编码

 

创建socket对象,

一个ip和一个端口只能被一个程序使用。端口只能进行一次监听,绑定,再次监听或者绑定的话就会报错。

使用完毕后必须进行关闭。

 

应用:

 

 

 

 

 

 

简单的实现:解决中文的情况,编解码的时候全部注明统一编码和解码。

import socket

server =socket.socket()
server.bind((‘127.0.0.1’,99))
server.listen(99)

s1,ip = server.accept()
print(s1)
print(ip)

while True:
data = s1.recv(1024)
print(data)
s1.send(‘ack{}’.format(data.decode(‘gbk’)).encode(‘gbk’))

s1.close()

server.close()

 

<socket.socket fd=192, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1’, 99), raddr=(‘127.0.0.1’, 50149)>

(‘127.0.0.1′, 50149)

b’\xd6\xd0\xb9\xfa’

客服端和服务器端建立间接,需要建立一条socket通道,每次建立连接,listen的端口都会和客户端建立的新的端口,因为连接端口就会阻塞,所以需要建立新的端口。隐士的还是看到连接的端口还是原来的socket。

 

 

 

 

 

应用:写一个群聊程序

1)第一步:import threading
import logging
import socket

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatServer:
def __init__(self,ip=’127.0.0.1′,port=999):
self.addr = (ip , port)
self.socket = socket.socket()

def start(self):
self.socket.bind(self.addr)
self.socket.listen()

threading.Thread(target=self.accept,name=’accept’).start()

def accept(self):
while True:
s,ip = self.socket.accept()
logging.info(s)
logging.info(ip)
threading.Thread(target=self.connt,name=’connt’,args=(s,)).start()

def connt(self,sockets):
while True:
data = sockets.recv(1024)
logging.info(data)
sockets.send(‘ack-{}’.format(data.decode()).encode())

def stop(self):
self.socket.close()

cs = ChatServer()
cs.start()

 

2)第二步

把所有的客户端的ip和端口保留在一个容器里面,一个客户端发送消息到服务器端,服务器端,进行消息的转发等。

 

import threading
import logging
import socket

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatServer:
def __init__(self,ip=’127.0.0.1′,port=999):
self.addr = (ip , port)
self.socket = socket.socket()
self.cliens = {}

def start(self):
self.socket.bind(self.addr)
self.socket.listen()

threading.Thread(target=self.accept,name=’accept’).start()

def accept(self):
while True:
s,ip = self.socket.accept()
logging.info(s)
logging.info(ip)
self.cliens[ip] = s
threading.Thread(target=self.connt,name=’connt’,args=(s,)).start()

def connt(self,sockets):
while True:
data = sockets.recv(1024)
logging.info(data)
sockets.send(‘ack-{}’.format(data.decode(‘gbk’)).encode(‘gbk’))
for s in self.cliens.values():
s.send(‘ack1-{}’.format(data.decode(‘gbk’)).encode(‘gbk’))

def stop(self):

for s in self.cliens.values():
s.close()

self.socket.close()

cs = ChatServer()
cs.start()

 

其他方法:

名称 含义
Socket.recv(bufsize[,flags]) 获取数据,默认阻塞的方式
Socket.recvfrom(bufsize[,flags]) 获取数据,返回一个二元组(bytes,address)
Socket.recv_into(buffer[,nbytes[,flags]]) 获取nbytes的数据后,存储到buffer中,如果nbytes没有指定或0,将buffer大小的数据存入buffer中,返回接受的字节数
Socket.recvfrom_into(buffer[,nbytes[,flags]]) 获取数据,返回一个二元组(bytes,address)到buffer中
Socket.send(bytes[,flags]) TCP 发送数据
Socket.sendall(bytes[,flags]) TCP发送全部数据,成功返回None
Socket.sendto(string[,flag],address) UDP发送数据
Socket.sendfile(file,offset=0,count=None) 发送一个文件直到EOF,使用高性能的os.sendfile机制,返回发送的字节数,如果win下不支持sendfile,或者不是普通文件,使用send()发送文件,offset告诉其实位置,3.5版本开始。

 

Makefile

Socket.makefile(mode=’r’,buffering=None,*,encoding=None,errors=None,newline=None)创建一个与该套接字相关连的文件对象,将recv方法看做读方法,将send方法看做是写方法。

异常不捕获会导致当前线程异常退出,不捕获直接到最外层,也就是主线程。

 

3、客户端tcp编程

import socket

raddr = (‘127.0.0.1′,999)
client = socket.socket()
client.connect(raddr)

while True:
data =  client.recv(1024)
print(data)
if data.strip() == b’quit’:
break
client.send(b’ack’)
client.close()

 

import threading
import socket
import logging
import datetime
FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatClient:
def __init__(self,ip=’127.0.0.1′,port=8080):
self.clients = socket.socket()
self.raddr = (ip,port)
self.event = threading.Event()

def start(self):
self.clients.connect(self.raddr)
self.send(‘I am ok’)

threading.Thread(target=self.recive,name=’receive’).start()

def recive(self):
while not self.event.is_set():
data = self.clients.recv(1024)
# logging.info(data)
if data.strip() == b’quit’:
break
message = ‘{:%Y/%m/%d %H:%M:%S}{}:{}\n{}\n’.format(datetime.datetime.now(),*self.raddr,data.strip())
logging.info(message)
def send(self,message:str):
data = ‘{}\n’.format(message.strip()).encode()
self.clients.send(data)

def stop(self):
self.event.set()
self.clients.close()

def main():
cc = ChatClient()
cc.start()

while True:
cmd = input(‘>>>>’)
if cmd.strip() == ‘quit’:
cc.stop()
break
cc.send(cmd)
logging.info(threading.enumerate())

if __name__ == ‘__main__’:
main()

 

 

 

4、udp编程

 

同一个协议下绑定同一个端口,才会有端口冲突。Udp不会真的连接。

 

Import socket

Server=socket.socket(type=)

Server.bind(laddr)绑定本地自己用的。

Server.recv(1024)

 

Data ,raddr = Server.recvfrom(1024)

Server.sendto(b’back’,raddr);后面的ip可以是不存在的,都会发送出去的。

 

Server.connect(raddr)后面才可以使用send。一般都是客户端向服务端连接用的。

 

 

 

 

服务器端代码:

import socket
import threading
import logging

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatServer:

def __init__(self,ip=’127.0.0.1′,port=9999):
self.addr = (ip,port)
self.sockets = socket.socket(type=socket.SOCK_DGRAM)
self.event = threading.Event()

def start(self):
self.sockets.bind(self.addr)
threading.Thread(target=self.recv,name=’recv’).start()

def recv(self):
while not self.event.is_set():
data,laddr = self.sockets.recvfrom(1024)
logging.info(data)
logging.info(laddr)

msg = ‘ack.{}from{}{}’.format(data.decode(),*laddr)
masg1 = msg.encode()
logging.info(msg)
self.sockets.sendto(masg1,laddr)

def stop(self):
self.sockets.close()
self.event.set()

def main():
cs = ChatServer()
cs.start()

while True:
cmd = input(‘>>>>’)
if cmd.strip() == ‘quit’:
cs.stop()
break
logging.info(threading.enumerate())

if __name__ == ‘__main__’:
main()

 

客户端代码:

import threading
import socket
import logging
import datetime

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatUdpClient:
def __init__(self,ip=’127.0.0.1′,port=8080):
self.addr = (ip,port)
self.cucsocket = socket.socket(type=socket.SOCK_DGRAM)
self.event = threading.Event()

def start(self):
self.cucsocket.connect(self.addr)
threading.Thread(target=self.recive,name=’recive’).start()

def recive(self):
while not self.event.is_set():
data,raddr =self.cucsocket.recvfrom(1024)
logging.info(data)
logging.info(raddr)

message = ‘{}from{}{}’.format(data.decode(),*raddr)

def send(self,message:str):
self.cucsocket.sendto(message.encode(), self.addr)

def stop(self):
self.cucsocket.close()
self.event.set()

def main():
cuc = ChatUdpClient()
cuc.start()

while True:
cmd = input(‘>>>’)
if cmd.strip() == ‘quit’:
cuc.stop()
break
cuc.send(cmd)
logging.info(threading.enumerate())

if __name__ == ‘__main__’:
main()

 

 

 

 

ack机制和心跳heartbeat。

 

心跳机制:

  • 一般来说客户端定时发往服务器端,服务器端并不需要ack回复客户端,只是需要记录客户端活着就可以了。(严格考虑时间的问题)
  • 服务器端定时发往客户端,一般需要客户端ack响应来表示活着,如果没有收到ack的客户端,服务端移除其信息,这种实现复杂,用的较少。

 

3)也可以是双向都发心跳包的,用的情况下较少。

 

为True的时候就不进入循环了。

 

 

心跳包客户端代码:

import threading
import socket
import logging
import datetime

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatUdpClient:
def __init__(self,ip=’127.0.0.1′,port=8080):
self.addr = (ip,port)
self.cucsocket = socket.socket(type=socket.SOCK_DGRAM)
self.event = threading.Event()

def start(self):
self.cucsocket.connect(self.addr)
threading.Thread(target=self.sen_hb,name=’hb’).start()
threading.Thread(target=self.recive,name=’recive’).start()

def recive(self):
while not self.event.is_set():
data,raddr =self.cucsocket.recvfrom(1024)
logging.info(data)
logging.info(raddr)

message = ‘{}from{}{}’.format(data.decode(),*raddr)

def send(self,message:str):
self.cucsocket.sendto(message.encode(), self.addr)

def sen_hb(self):
self.send(‘hb’)

def stop(self):
self.cucsocket.close()
self.event.set()

def main():
cuc = ChatUdpClient()
cuc.start()

while True:
cmd = input(‘>>>’)
if cmd.strip() == ‘quit’:
cuc.stop()
break
cuc.send(cmd)
logging.info(threading.enumerate())

if __name__ == ‘__main__’:
main()

 

心跳包服务器端代码:

import threading
import socket
import logging
import datetime

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class ChatUdpServer:
def __init__(self,ip=’127.0.0.1′,port=8080,interval=10):
self.addr = (ip,port)
self.udpsocket = socket.socket(type=socket.SOCK_DGRAM)
self.event = threading.Event()
self.interval = interval
self.clients = {}

def start(self):
self.udpsocket.bind(self.addr)
threading.Thread(target=self.revice,name=’recive’).start()

def revice(self):
while not self.event.is_set():
lset = set()
data,raddr = self.udpsocket.recvfrom(1024)
logging.info(data)
logging.info(raddr)

current = datetime.datetime.now().timestamp()
if data.strip() == b’hb’:
self.clients[raddr]=current
continue
elif data.strip() == b’quit’:  #有可能发出来的数据不在clients。
self.clients.pop(raddr,None)
logging.info(‘{}leaving’.format(raddr))
continue
self.clients[raddr] = current

message = ‘{}form{}{}’.format(data.decode(),*raddr)

for c ,stamp in self.clients.items():
if current – stamp >self.interval:
lset.add(c)
else:
self.udpsocket.sendto(message.encode(), raddr)

for c in lset:
self.clients.pop(c)

def stop(self):
self.event.set()
self.udpsocket.close()

def main():

cus = ChatUdpServer()
cus.start()

while True:
cmd = input(‘>>>>’)
if cmd == ‘quit’:
cus.stop()
break
logging.info(threading.enumerate())

if __name__ == ‘__main__’:
main()

 

 

正在迭代字典的时候不能进行pop。。

将其添加到set中,之后再进行pop。

 

Udp协议应用:

是无连接协议,基于以下假设:网络足够好 消息不会丢包,包不会乱序。

但是,即使在局域网,也不能保证不丢包,而且包到达不一定有序。

应用场景在视频、音频传输,一般来说,丢些包,问题不大,最多丢图像,听不清话语,可以重新发话语来解决,海量采集数据,例如传感器发来的数据,丢十几,几百条没有太大问题,DNS协议,数据内容小,一个包能 查询到结果,不存在乱序,丢包,重新请求解析。

 

Udp性能优于tcp,但是可靠性场所适用于tcp。

Udp广域网。

 

二、Socketserver

Socket编程过于底层,Python中对api进行封装的就是socketserver模块,是网络编程框架,便于企业快速开发;

 

类的继承关系

 

 

Socketserver简化了网络服务器的编写。

有四个同步类:TCPserver,UDPserver,Unixstreamserver,Unixdatagramserver

2个Mixin类:ForkingMixin和threadingMixin类,用来支持异步

Class forKingUDPserver(forKingMixin,UDPserver):pass

Class forKingTCPserver(forKingMixin,TCPPserver):pass

Class ThreadingUDPserver(ThreadingMixin,UDPserver):pass

Class ThreadingTCPserver(ThreadingMixin,TCPserver):pass

class BaseServer:

def __init__(self, server_address, RequestHandlerClass):
“””Constructor.  May be extended, do not override.”””
     self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.__is_shut_down = threading.Event()
self.__shutdown_request = False

def finish_request(self, request, client_address):
“””Finish one request by instantiating RequestHandlerClass.”””
     self.RequestHandlerClass(request, client_address, self)

 

baserequesthandler类:

他是和用户连接的用户请求处理类的基类,定义为baserequesthandler(request,client_address,server)

 

服务器端server实例接受用户的请求偶,最后会实例化这个类。

被初始化以后,送入三个构造参数,request,client_address,server 本身。

以后就可以在baserequesthandler类的实例上使用以下属性:

Self.request是和客户端的连接的socket对象,

Self.server是TCPserver本身

Self.client_address是客户端地址。

 

这个类在初始化的过程中,会依次调用3个方法,子类可以覆盖这些方法

class BaseRequestHandler:

“””Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define other arbitrary instance variables.

    “””

    def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()

def setup(self):
pass

def handle(self):
pass

def finish(self):
pass

 

import threading
import socketserver

class Myhandler(socketserver.BaseRequestHandler):
def handle(self):
print(‘——–‘)
print(self.server)
print(self.request)
print(self.client_address)
print(self.__dict__)
print(self.server.__dict__)

print(threading.enumerate())
print(threading.current_thread())

addr = (‘127.0.0.1’,8080)
server = socketserver.ThreadingTCPServer(addr,Myhandler)

server.serve_forever()

 

 

import threading
import socketserver
import logging

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class Myhandler(socketserver.BaseRequestHandler):
def handle(self):
print(‘——–‘)
print(self.server)
print(self.request)
print(self.client_address)
print(self.__dict__)
print(self.server.__dict__)

print(threading.enumerate())
print(threading.current_thread())
for i in range(2):
data = self.request.recv(1024)
logging.info(data)

addr = (‘127.0.0.1’,8080)
server = socketserver.ThreadingTCPServer(addr,Myhandler)

server.serve_forever()

 

类命名:

 

编程接口:

Socketserver.baseserver(server_address,RequestHandlerclass)

需要提供服务器绑定的地址信息,和用于请求处理请求的requesthandlerclass类。

Requesthandlerclass类必须是baserequesthandler类的子类,在baseserver中代码如下:

 

创建、传端口、handler。

ThreadingTCPServer。多线程,异步的,同时处理多个连接,

TCPServer  TCP的,串行的。同步的,一个处理完毕后,才能处理下一个。只有主线程。

 

创建服务器需要几个步骤:

1)从baseRequestHandler类派生出子类,并覆盖其handler()方法来创建请求处理程序类,此方法将处理传入处理。

2)实例化一个服务器类,传参服务器的地址和请求处理类。

3)调用服务器实例的handle_request()或server_forever()方法。

4)调用server_close()关闭套接字。

 

实现echoserver

import threading
import socketserver
import logging
import sys

FORMAT = ‘%(asctime)s %(threadName)s %(thread)d %(message)s’
logging.basicConfig(format=FORMAT,level=logging.INFO)

class EchoHandler(socketserver.BaseRequestHandler):
clients = {}

def setup(self):
self.event = threading.Event()
self.clients[self.client_address] = self.request

def finish(self):
self.event.set()

def handle(self):
while not self.event.is_set():
data = self.request.recv(1024)
logging.info(data)
if data == b” or data ==’quit’:
break
msg = ‘{}’.format(data.decode())
for c in self.clients.values():
c.send(msg.encode())

addr = (‘127.0.0.1′,8080)
server = socketserver.ThreadingTCPServer(addr,EchoHandler)

# server.serve_forever()
t = threading.Thread(target=server.serve_forever,name=’encho’)
t.start()

if __name__ == ‘__main__’:

try:
while True:
cmd = input(‘>>>’)
if cmd.strip() == ‘quit’:
server.server_close()
break
logging.info(threading.enumerate())
except Exception as e:
logging.info(e)

finally:
print(‘exit’)
sys.exit(0)

 

解决客户端主动断开连接服务器端报错的方式:客户端主动断开,会导致recv方法会立即返回一个空bytes,并没有同事抛出异常,当循环到recv这一句的时候就会抛出异常,所以,可以通过判断data数据是否为空客户端是否断开。

 

 

总结:

为每一个连接提供requesthandlerclass类实例,依次调用setup、handler、finish方法,且使用了try..finally结构,保证finish方法一定被调用、这些方法一次执行完毕,如果想维持这个连接和客户端通信,就需要在handler函数中循环。

 

所持socketserver模块提供不同的类,但是编程接口一样的,即使是多进程、多线程的类也是一样,大大减少了编程的难度。

 

 

三、同步编程

1)同步、异步

函数或方法被调用的时候,调用者是否得到最终的结果。

直接得到最终结果的结果,就是同步调用。(打饭模型,打饭不打好不走开,直到打饭给我后才离开)

不直接得到的最终的结果,就是异步调用。(打饭,不会一直等着,会时不时的过来看看,打完了把饭拿走,异步不保证多长时间打完了饭)

 

2)阻塞、非阻塞:

函数或方法调用的时候,是否立即返回。

立即返回就是非阻塞调用。

不立即返回就是阻塞调用。

 

3)区别:

同步、异步,与阻塞、非阻塞不相关。

同步、异步强调的是结果。

阻塞和非阻塞强调的是时间,是否等待。

 

 

同步与异步区别在于:调用者是否得到了想要的最终结果。

同步就是一直要执行到返回最终结果。

异步就是直接返回了,但是返回的不是最终的结果,调用者不能通过这种调用得到结果,还要通过被调用者,使用其他方式通知调用者,来取回最终结果。

 

阻塞与非阻塞的区别在于,调用者是否还能干其他的事情。

阻塞,调用者只能干等。

非阻塞,调用者可以先忙一会别的,不用一直等。

 

4)联系:

同步阻塞:调用者阻塞,直到等到拿到最终结果。(打饭模型,什么事情也不敢,就等着打饭,打饭是结果,什么也不干,一直在等着,同步加阻塞)

同步非阻塞:(等着打饭,但是可以玩会手机,看看电视,打饭是结果,但是不用一直在等着)

 

异步阻塞:(我要打饭,你说等着较好,并没有返回饭给我,我啥事不干,就干等着饭好了叫我)

异步非阻塞:回调的话。(我要打饭,你说等较好,并没有返回饭给我,可以在旁边看看电视,玩玩手机,饭好了叫我)

 

5)同步IO、异步IO、IO多路复用

IO模型:

IO分为两个阶段。

  • 数据准备阶段。
  • 内核空间复制回用户进程缓冲区阶段。

发生IO的时候:

  • 内核从输入设备读、写数据(淘米,把米放锅里煮饭)
  • 进程从内核复制数据(盛饭,从内核这个锅把饭装到碗里面来)

 

系统调用  — read函数

 

从磁盘读取到内核空间中来,在拷贝到用户的应用空间内。

 

系统调用read函数。内核空间,用户空间。

5)IO模型

IO模型:同步IO,包括阻塞IO,非阻塞IO,IO多路复用。

阻塞IO

 

进程等待(阻塞),直到读写完成。(全程等待) read/write函数

 

进程调用read操作,如果IO设备没有准备好,立即返回error,进程不阻塞,用户可以再次 发起系统调用,如果内核已经准备好了,就阻塞,然后复制数据到用户空间。

 

第一阶段数据没有准备好,就先忙别的,等来再来看看,检查数据是否准备好了的过程是非阻塞的。

第二阶段是阻塞的,即内核空间和用户空间之间复制数据是阻塞的。

淘米、蒸饭不用等,去完后。盛饭过程等着你装好饭,但是要等到盛好饭才算完事,这个是同步的,结果就是盛好饭。

Read/write

 

 

IO多路复用:

IO多路复用,就是同时监控多个IO,有一个准备好了,就不需要等了开始处理,提高了同同时处理IO的能力。

Select几乎所有操作系统平台都支持,poll是对select的升级。

epoll,Linux系统内核2.5+开始的。对select和poll的增强,在监视的基础上,增加回调机制,BSD。Mac平台都有kqueue,window是有iocp

 

 

回调:

Select为例,将关注的IO操作告诉select函数并调用,进程阻塞,内核监视select关注的文件描述符fd,被关注的任何一个fd对应的IO准备好了数据,select返回,在使用read将数据复制到用户进程。

 

 

Select:最多监听1024个IO。IO多了,每次都要遍历全部发送,效率低下。

epoll:通知机制。没有fd的上限,且是回调机制。效率很高。

 

 

 

 

 

 

6)异步IO:

 

 

异步的性能是非常高的。

进程发起异步IO请求,立即返回,内核完成IO的两个阶段,内核给进程发一个信号。

举例:今天不想出去吃饭了,点外卖,饭菜在饭店做好了(第一阶段),送餐员会从饭店送到家门口(第二阶段)

 

 

 

7)Python中IO多路复用:

IO多路复用:

大多数操作系统支持select和poll

Linux2.5+支持epoll

BSD、mac支持kqueue。

Windows的IOcp

 

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/101095

发表评论

登录后才能评论

This site uses Akismet to reduce spam. Learn how your comment data is processed.

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code