io多路復用:沒有使用多程序和多執行緒的情況下完成多個套接字的使用。
select 能夠完成一些套接字的檢查,從頭到尾檢查一遍後,標記哪些套接字是否可以收資料,返回的時候,就返回能接收資料的套接字,返回的是列表。select是由作業系統提供的,效率要高些,非常快的方式檢測哪些套接字可以接收資料。select是跨平台的,在window也可以用。
網路通訊被unix系統抽象為檔案的讀寫,通常是乙個裝置,由裝置驅動程式提供,驅動可以知道自身的資料是否可用。支援阻塞操作的裝置驅動通常會實現一組自身的等待佇列,如讀/寫等待佇列用於支援上層(使用者層)所需的block或non-block操作。裝置的檔案的資源如果可用(可讀或者可寫)則會通知程序,反之則會讓程序睡眠,等到資料到來可用的時候,再喚醒程序。
這些裝置的檔案描述符被放在乙個陣列中,然後select呼叫的時候遍歷這個陣列,如果對於的檔案描述符可讀則會返回改檔案描述符。當遍歷結束之後,如果仍然沒有乙個可用裝置檔案描述符,select讓使用者程序則會睡眠,直到等待資源可用的時候在喚醒,遍歷之前那個監視的陣列。每次遍歷都是依次進行判斷的。
select()所維護的儲存大量檔案描述符的資料結構,隨著檔案描述符數量的增大,其複製的開銷也線性增長。同時,由於網路響應時間的延遲使得大量
tcp連線處於非活躍狀態,但呼叫
select()
會對所有
socket
進行一次線性掃瞄,所以這也浪費了一定的開銷。
1)select的乙個缺點在於單個程序能夠監視的檔案描述符的數量存在最大限制,linux上一般為1024,可以通過修改巨集定義甚至重新編譯核心的方式提公升這一限制,但是這樣也會造成效率的降低。一般來說這個數目和系統記憶體關係很大,具體數目可以cat /proc/sys/fs/file-max察看。32位機預設是1024個。64位機預設是2048
2)
對socket進行掃瞄時是依次掃瞄的,即採用輪詢的方法,效率較低。
3)當套接字比較多的時候,每次select()都要通過遍歷fd_setsize個socket來完成排程,不管哪個socket是活躍的,都遍歷一遍。這會浪費很多cpu時間。
from socket import af_inet,socket,so_reuseaddr,sock_stream,sol_socket
from select import select
import sys
def main():
#建立tcp的socket套接字
server_socket = socket(af_inet,sock_stream)
server_socket.setsockopt(sol_socket,so_reuseaddr,1)
#繫結埠
server_socket.bind(("",9999))
#設定監聽
server_socket.listen(5)
#客戶端列表
socket_lists = [server_socket,sys.stdin]
wirte_list =
#是否退出
is_run = false
try:
while true:
#檢測列表client_lists那些socket可以接收資料,
#檢測列表那些套接字(socket)可否傳送資料
#檢測列表那些套接字(socket)是否產生了異常
print("select--111")
#這個select函式預設是堵塞,當有客戶端鏈結的時候解除阻塞,
# 當有資料可以接收的時候解除阻塞,當客戶端斷開的時候解除阻塞
readable, wirteable,excep = select(socket_lists,wirte_list,)
# print("select--2222")
# print(111)
for sock in wirteable:
#這個會一直傳送,因為他是處於已經發的狀態
sock.send("thank you!".encode("gb2312"))
for sock in readable:
#接收資料
if sock == server_socket:
print("sock == server_socket")
#有新的客戶端鏈結進來
new_socket,new_address = sock.accept()
#新的socket新增到列表中,便於下次socket的時候能檢查到
elif sock == sys.stdin:
cmd = sys.stdin.readline()
print(cmd)
is_run = cmd
else:
# print("sock.recv(1024)....")
#此時的套接字sock是直接可以取資料的
recv_data = sock.recv(1024)
if len(recv_data) > 0:
print("從[%s]:%s" % (str(new_address),recv_data))
sock.send(recv_data)
#把鏈結上有訊息接收的socket新增到監聽寫的列表中
else:
print("客戶端已經斷開")
#客戶端已經斷開,要移除
sock.close()
socket_lists.remove(sock)
#是否退出程式
if is_run:
break
finally:
#關閉套接字
server_socket.close()
if __name__ == "__main__":
main()
3. 其他
select -->最多1024個套接字-->採用輪詢方式程序檢測套接字是否可以接收等
poll -->解決了支援套接字上線問題-->採用輪詢方式程序檢測
epoll-->解決支援上限問題-->採用的是事件通知
單程序tcp伺服器 epoll版
1 沒有最大併發連線的限制,能開啟的fd 指的是檔案描述符,通俗的理解就是套接字對應的數字編號 的上限遠大於1024。2 效率提公升,不是輪詢的方式,不會隨著fd數目的增加效率下降。只有活躍可用的fd才會呼叫callback函式 即epoll最大的優點就在於它只管你 活躍 的連線,而跟連線總數無關,...
單程序伺服器
1.完成乙個簡單的tcp伺服器 from socket import sersocket socket af inet,sock stream 重複使用繫結的資訊 sersocket.setsockopt sol socket,so reuseaddr 1 localaddr 7788 sersoc...
socket程式設計之TCP單程序的伺服器
今天介紹的是基於ipv4的socket網路程式設計,我們知道socket api是一層的抽象的網路程式設計介面,但各網路協議的位址卻是各不相同的。下圖是sockaddr資料結構圖 ipv4和ipv6的位址格式定義在netinet in.h中,ipv4位址用sockaddr in結構體表示,包括16位...