單程序tcp伺服器 epoll版

2021-08-19 09:40:56 字數 3065 閱讀 5515

1)沒有最大併發連線的限制,能開啟的fd(指的是檔案描述符,通俗的理解就是套接字對應的數字編號)的上限遠大於1024。

2)效率提公升,不是輪詢的方式,不會隨著fd數目的增加效率下降。只有活躍可用的fd才會呼叫callback函式;即epoll最大的優點就在於它只管你「活躍」的連線,而跟連線總數無關,因此在實際的網路環境中,epoll的效率就會遠遠高於select和poll。

3)epoll採用的事件通知機制

1)linux核心(kernel)利用檔案描述符(file descriptor)來訪問檔案。檔案描述符是非負整數。開啟現存盤案或新建檔案時,核心會返回乙個檔案描述符。讀寫檔案也需要使用檔案描述符來指定待讀寫的檔案。

2)檔案描述符在形式上是乙個非負整數。實際上,它是乙個索引值,指向核心為每乙個程序所維護的該程序開啟檔案的記錄表。當程式開啟乙個現有檔案或者建立乙個新檔案時,核心向程序返回乙個檔案描述符。在程式設計中,一些涉及底層的程式編寫往往會圍繞著檔案描述符展開。但是檔案描述符這一概念往往 只適用於unix、linux這樣的作業系統。windows沒有。

3)習慣上,標準輸入 (standard input) 的檔案描述符是 0,標準輸出 (standard output) 是 1,標準錯誤(standard error) 是 2。儘管這種習慣並非unix核心的特性,但是因為一些 shell 和很多應用程式都使用這種習慣,因此,如果核心不遵循這種習慣的話,很多應用程式將不能使用。

水平觸發:將就緒的檔案描述符告訴程序後,如果程序沒有對其進行io操作,那麼下次呼叫epoll時將再次報告這些檔案描述符,這種方式稱為水平觸發. 

邊緣觸發:只告訴程序哪些檔案描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有採取行動,那麼它將不會再次告知,這種方式稱為邊緣觸發。

理論上邊緣觸發的效能要更高一些,但是**實現相當複雜。

epoll對檔案描述符的操作有兩種模式:lt(level trigger)和et(edge trigger)。

lt模式是預設模式

,lt模式與et模式的區別如下:

lt模式:當epoll檢測到描述符事件發生並將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫epoll時,會再次響應應用程式並通知此事件。

直到你出來為止。

et模式:當epoll檢測到描述符事件發生並將此事件通知應用程式,應用程式必須立即

處理該事件。下次將不會在通知。

et要比lt效率高

from socket import *

import select

def main():

#建立tcp伺服器套接字

server_socket = socket(af_inet,sock_stream)

#設定埠可以重用

server_socket.setsockopt(sol_socket,so_reuseaddr,1)

#繫結埠

server_socket.bind(("",9999))

#設定監聽

server_socket.listen(5)

#用epoll設定監聽收資料

epoll = select.epoll()

#把server_socket註冊到epoll的事件監聽中,如果已經註冊過會發生異常

epoll.register(server_socket.fileno(),select.epollin|select.epollet)

#裝socket列表

socket_lists = {}

#裝socket對應的位址

socket_address = {}

while true:

#返回套接字列表[(socket的檔案描述符,select.epollin)],

# 如果有新的鏈結,有資料發過來,斷開鏈結等都會解除阻塞

print("epoll.poll--111")

epoll_list = epoll.poll()#[(檔案描述符,註冊的事件)]

print("epoll.poll--222")

print(epoll_list)

for fd,event in epoll_list:

#有新的鏈結

if fd == server_socket.fileno():

print("新的客戶fd==%s" % fd)

new_sokect,new_address = server_socket.accept()

#往字典新增資料

socket_lists[new_sokect.fileno()] = new_sokect

socket_address[new_sokect.fileno()] = new_address

#註冊新的socket也註冊到epoll的事件監聽中

epoll.register(new_sokect.fileno(), select.epollin | select.epollet)

elif event ==select.epollin:

print("收到資料了")

#根據檔案操作符取出對應socket

new_sokect = socket_lists[fd]

address = socket_address[fd]

recv_data = new_sokect.recv(1024)

if len(recv_data) > 0:

print("已經收到[%s]:%s" % (str(address),recv_data.decode("gb2312")))

else:

#客戶端埠,取消監聽

epoll.unregister(fd)

#關閉鏈結

new_sokect.close()

print("[%s]已經下線" % str(address))

#關閉套接字鏈結

server_socket.close()

if __name__ == "__main__":

main()

單程序tcp伺服器 select版

io多路復用 沒有使用多程序和多執行緒的情況下完成多個套接字的使用。select 能夠完成一些套接字的檢查,從頭到尾檢查一遍後,標記哪些套接字是否可以收資料,返回的時候,就返回能接收資料的套接字,返回的是列表。select是由作業系統提供的,效率要高些,非常快的方式檢測哪些套接字可以接收資料。sel...

TCP伺服器實現epoll併發實現

原始碼在最下方 併發伺服器 tcp伺服器百萬級連線 申請乙個int sockfd socket af inet,sock stream,0 初始化乙個例項sockaddr in struct sockaddr in addr memset addr,0,sizeof struct sockaddr ...

單程序伺服器

1.完成乙個簡單的tcp伺服器 from socket import sersocket socket af inet,sock stream 重複使用繫結的資訊 sersocket.setsockopt sol socket,so reuseaddr 1 localaddr 7788 sersoc...