日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

pythonselect a valied_Python Select 解析

發布時間:2023/12/19 python 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 pythonselect a valied_Python Select 解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

sellect、poll、epoll三者的區別

一、select

select目前幾乎在所有的平臺上支持,其良好跨平臺支持也是它的一個優點,事實上從現在看來,這也是它所剩不多的優點之一。

select的一個缺點在于單個進程能夠監視的文件描述符的數量存在最大限制,在Linux上一般為1024,不過可以通過修改宏定義甚至重新編譯內核的方式提升這一限制。

另外,select()所維護的存儲大量文件描述符的數據結構,隨著文件描述符數量的增大,其復制的開銷也線性增長。同時,由于網絡響應時間的延遲使得大量TCP連接處于非活躍狀態,但調用select()會對所有socket進行一次線性掃描,所以這也浪費了一定的開銷。

二、poll

poll和select同樣存在一個缺點就是,包含大量文件描述符的數組被整體復制于用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨著文件描述符數量的增加而線性增大。

另外,select()和poll()將就緒的文件描述符告訴進程后,如果進程沒有對其進行IO操作,那么下次調用select()和poll()的時候將再次報告這些文件描述符,所以它們一般不會丟失就緒的消息,這種方式稱為水平觸發(Level Triggered)。

三、epoll

epoll可以同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有采取行動,那么它將不會再次告知,這種方式稱為邊緣觸發),理論上邊緣觸發的性能要更高一些,但是代碼實現相當復雜。

epoll同樣只告知那些就緒的文件描述符,而且當我們調用epoll_wait()獲得就緒文件描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的文件描述符即可,這里也使用了內存映射(mmap)技術,這樣便徹底省掉了這些文件描述符在系統調用時復制的開銷。

另一個本質的改進在于epoll采用基于事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法后,內核才對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來注冊一個文件描述符,一旦基于某個文件描述符就緒時,內核會采用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便得到通知。

Python select

Python的select()方法直接調用操作系統的IO接口,它監控sockets,open files, and pipes(所有帶fileno()方法的文件句柄)何時變成readable 和writeable, 或者通信錯誤,select()使得同時監控多個連接變的簡單,并且這比寫一個長循環來等待和監控多客戶端連接要高效,因為select直接通過操作系統提供的C的網絡接口進行操作,而不是通過Python的解釋器。

備注:?Using Python’s file objects with select() works for Unix, but is not supported under Windows.

接下來通過server例子要以了解select 是如何通過單進程實現同時處理多個非阻塞的socket連接的

#!/usr/bin/env python

#-*- coding:utf-8 -*-

import select,socket,sys,Queue

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.setblocking(0) #設置非阻塞模式,參數也可以是False

server_address = ('localhost', 10000)

print('starting up on %s port %s' % server_address)

server.bind(server_address)

# Listen for incoming connections

server.listen(5)

select()方法接收并監控3個通信列表, 第一個是所有的輸入的data,就是指外部發過來的數據,第2個是監控和接收所有要發出去的data(outgoing data),第3個監控錯誤信息,接下來我們需要創建2個列表來包含輸入和輸出信息來傳給select().

# Sockets from which we expect to read

inputs = [ server ]

# Sockets to which we expect to write

outputs = [ ]

所有客戶端的進來的連接和數據將會被server的主循環程序放在上面的list中處理,我們現在的server端需要等待連接可寫(writable)之后才能過來,然后接收數據并返回(因此不是在接收到數據之后就立刻返回),因為每個連接要把輸入或輸出的數據先緩存到queue里,然后再由select取出來再發出去。

# Outgoing message queues (socket:Queue)

message_queues = {}

下面是此程序的主循環,調用select()時會阻塞和等待直到新的連接和數據進來

while inputs:

# Wait for at least one of the sockets to be ready for processing

print('waiting for the next event')

readable, writable, exceptional = select.select(inputs, outputs, inputs [,timeout])

當你把inputs,outputs,exceptional(這里跟inputs共用)傳給select()后,它返回3個新的list,我們上面將他們分別賦值為readable,writable,exceptional, 所有在readable list中的socket連接代表有數據可接收(recv),所有在writable list中的存放著你可以對其進行發送(send)操作的socket連接,當連接通信出現error時會把error寫到exceptional列表中。

readable list 中的socket 可以有3種可能狀態,第一種是如果這個socket是main “server” socket,它負責監聽客戶端的連接,如果這個main server socket出現在readable里,那代表這是server端已經ready來接收一個新的連接進來了,為了讓這個main server能同時處理多個連接,在下面的代碼里,我們把這個main server的socket設置為非阻塞模式。

# Handle inputs

for s in readable:

if s is server:

# A "readable" server socket is ready to accept a connection

connection, client_address = s.accept()

print('new connection from', client_address)

connection.setblocking(0)

inputs.append(connection)

# Give the connection a queue for data we want to send

message_queues[connection] = Queue.Queue()

第二種情況是這個socket是已經建立了的連接,它把數據發了過來,這個時候你就可以通過recv()來接收它發過來的數據,然后把接收到的數據放到queue里,這樣你就可以把接收到的數據再傳回給客戶端了。

else:

data = s.recv(1024)

if data:

# A readable client socket has data

print('received "%s" from %s' % (data, s.getpeername()))

message_queues[s].put(data)

# Add output channel for response

if s not in outputs:

outputs.append(s)

第三種情況就是這個客戶端已經斷開了,所以你再通過recv()接收到的數據就為空了,所以這個時候你就可以把這個跟客戶端的連接關閉了。

else:

# Interpret empty result as closed connection

print('closing', client_address, 'after reading no data')

# Stop listening for input on the connection

if s in outputs:

outputs.remove(s) #既然客戶端都斷開了,我就不用再給它返回數據了,所以這時候如果這個客戶端的連接對象還在outputs列表中,就把它刪掉

inputs.remove(s) #inputs中也刪除掉

s.close() #把這個連接關閉掉

# Remove message queue

del message_queues[s]

對于writable list中的socket,也有幾種狀態,如果這個客戶端連接在跟它對應的queue里有數據,就把這個數據取出來再發回給這個客戶端,否則就把這個連接從output list中移除,這樣下一次循環select()調用時檢測到outputs list中沒有這個連接,那就會認為這個連接還處于非活動狀態

# Handle outputs

for s in writable:

try:

next_msg = message_queues[s].get_nowait()

except Queue.Empty:

# No messages waiting so stop checking for writability.

print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'

outputs.remove(s)

else:

print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())

s.send(next_msg)

最后,如果在跟某個socket連接通信過程中出了錯誤,就把這個連接對象在inputs\outputs\message_queue中都刪除,再把連接關閉掉

# Handle "exceptional conditions"

for s in exceptional:

print >>sys.stderr, 'handling exceptional condition for', s.getpeername()

# Stop listening for input on the connection

inputs.remove(s)

if s in outputs:

outputs.remove(s)

s.close()

# Remove message queue

del message_queues[s]

客戶端

這個是客戶端程序展示了如何通過select()對socket進行管理并與多個連接同時進行交互

import socket

import sys

messages = [ 'This is the message. ',

'It will be sent ',

'in parts.',

]

server_address = ('localhost', 10000)

# Create a TCP/IP socket

socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),

socket.socket(socket.AF_INET, socket.SOCK_STREAM),

]

# Connect the socket to the port where the server is listening

print('connecting to %s port %s' % server_address)

for s in socks:

s.connect(server_address)

接下來通過循環通過每個socket連接給server發送和接收數據。

for message in messages:

# Send messages on both sockets

for s in socks:

print('%s: sending "%s"' % (s.getsockname(), message))

s.send(message)

# Read responses on both sockets

for s in socks:

data = s.recv(1024)

print('%s: received "%s"' % (s.getsockname(), data))

if not data:

print('closing socket', s.getsockname())

完整代碼

一、服務器端代碼

#!/usr/bin/env python

#_*_coding:utf-8_*_

#env:python3.x

import select

import socket

import sys

import queue

# Create a TCP/IP socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.setblocking(False)

# Bind the socket to the port

server_address = ('localhost', 10000)

print('starting up on %s port %s' % server_address)

server.bind(server_address)

# Listen for incoming connections

server.listen(5)

# Sockets from which we expect to read

inputs = [ server ]

# Sockets to which we expect to write

outputs = [ ]

message_queues = {}

while inputs:

# Wait for at least one of the sockets to be ready for processing

print('\nwaiting for the next event')

readable, writable, exceptional = select.select(inputs, outputs, inputs)

# Handle inputs

for s in readable:

if s is server:

# A "readable" server socket is ready to accept a connection

connection, client_address = s.accept()

print('new connection from', client_address)

connection.setblocking(False)

inputs.append(connection)

# Give the connection a queue for data we want to send

message_queues[connection] = queue.Queue()

else:

data = s.recv(1024)

if data:

# A readable client socket has data

print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()) )

message_queues[s].put(data)

# Add output channel for response

if s not in outputs:

outputs.append(s)

else:

# Interpret empty result as closed connection

print('closing', client_address, 'after reading no data')

# Stop listening for input on the connection

if s in outputs:

outputs.remove(s) #既然客戶端都斷開了,我就不用再給它返回數據了,所以這時候如果這個客戶端的連接對象還在outputs列表中,就把它刪掉

inputs.remove(s) #inputs中也刪除掉

s.close() #把這個連接關閉掉

#Remove message queue

del message_queues[s]

# Handle outputs

for s in writable:

try:

next_msg = message_queues[s].get_nowait()

except queue.Empty:

# No messages waiting so stop checking for writability.

print('output queue for', s.getpeername(), 'is empty')

outputs.remove(s)

else:

print( 'sending "%s" to %s' % (next_msg, s.getpeername()))

s.send(next_msg)

# Handle "exceptional conditions"

for s in exceptional:

print('handling exceptional condition for', s.getpeername() )

# Stop listening for input on the connection

inputs.remove(s)

if s in outputs:

outputs.remove(s)

s.close()

# Remove message queue

del message_queues[s]

二、客戶端代碼

#!/usr/bin/env python

#_*_coding:utf-8_*_

import socket

import sys

messages = [ 'This is the message. ',

'It will be sent ',

'in parts.',

]

server_address = ('localhost', 10000)

# Create a TCP/IP socket

socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),

socket.socket(socket.AF_INET, socket.SOCK_STREAM),

]

# Connect the socket to the port where the server is listening

print 'connecting to %s port %s' % server_address

for s in socks:

s.connect(server_address)

for message in messages:

# Send messages on both sockets

for s in socks:

print '%s: sending "%s"' % (s.getsockname(), message)

s.send(message)

# Read responses on both sockets

for s in socks:

data = s.recv(1024)

print('%s: received "%s"' % (s.getsockname(), data))

if not data:

print('closing socket', s.getsockname())

s.close()

總結

以上是生活随笔為你收集整理的pythonselect a valied_Python Select 解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。