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.www58058.com/101095

(0)
604603701@qq.com604603701@qq.com
上一篇 2018-06-18 19:08
下一篇 2018-06-18

相關推薦

  • 樹 非線性結構,每個元素都可以有多個前驅和后繼 樹是n(n>=0)個元素 n = 0時,稱為空樹 樹只有一個特殊的沒有前驅的元素,稱為樹的根root 樹中除了根結點,其余元素只能有一個前驅,可以有0個或者多個后繼 遞歸定義 樹T是n(n>=0)個元素的集合,n=0時,稱為空樹 有且只有一個特殊元素根,剩余元素都可以被劃分為m個互不相交的集合T1,…

    2018-04-16
  • Ipython封裝解構

    IPython Shell命令 !command 執行shell命令 !ls -l , !touch a.txt file = !ls -l | grep py 魔術方法 使用%開頭的,IPython內置的特殊方法 %magic 格式 %開頭是line magic %% 開頭是cell magic,notebook的cell %alias 定義一個系統命令的…

    2018-04-09
  • 楊輝三角專題

    楊輝三角;二項式

    2018-04-09
  • 基礎語法

    基礎語法、判斷、循環

    2018-03-26
  • 函數

    函數 數學定義:y=f(x),y是x的函數,x是自變量 Python函數 有若干個語句塊,函數名稱,參數列表構成,它是組織代碼的最小單元 完成一定作用 函數的作用 結構化編程對代碼的最基本的封裝,一般按照功能組織一段代碼 封裝的目的為了復用,減少了冗余代碼 代碼更加簡潔美觀,更加易讀 函數的分類 內建函數,如max(),reversed()等 庫函數,如ma…

    2018-04-16
  • 正則表達式

    正則表達式

    Python筆記 2018-05-03
欧美性久久久久