epoll真正實現高並發服務器


epoll真正實現高並發服務器

epoll是IO模型中的一種,屬於多路復用IO模型;

select也是一種多路復用的IO模型,但是其單個select最多只能同時處理1024個socket,效率實在算不上高

注意:epoll僅在linux中可用

select實現並發的思路:

1.當網卡收到數據后會現將數據寫入到緩沖區

2.發送中斷信號給CPU

3.CPU執行中斷程序,將數據從內核copy到socket的緩沖區

4.喚醒進程,即將進程A切換到就緒態,同時從socket的等待隊列中移除這個進程引用

從上面的過程中不難看出

1.select,需要遍歷socket列表,頻繁的對等待隊列進行添加移除操作,

2.數據到達后還需要給變量所有socket才能獲知哪些socket有數據

兩個操作消耗的時間隨着要監控的socket的數量增加而大大增加,

處於效率考慮才規定了最大只能監視1024個socket

epoll基於select的基礎上實現並發思路:

1.創建epoll對象,epoll也會對應一個文件,由文件系統管理

2.執行register時,將epoll對象 添加到socket的等待隊列中

3.數據到達后,CPU執行中斷程序,將數據copy給socket

4.在epoll中,中斷程序接下來會執行epoll對象中的回調函數,傳入就緒的socket對象

5.將socket,添加到就緒列表中

6.喚醒epoll等待隊列中的進程,

進程喚醒后,由於存在就緒列表,所以不需要再遍歷socket了,直接處理就緒列表即可

解決了這兩個問題后,並發量得到大幅度提升,最大可同時維護上萬級別的socket

#epoll相關函數
import select 導入select模塊

epoll = select.epoll() 創建一個epoll對象

epoll.register(文件句柄,事件類型) 注冊要監控的文件句柄和事件

事件類型:

  select.EPOLLIN    可讀事件

  select.EPOLLOUT   可寫事件

  select.EPOLLERR   錯誤事件

  select.EPOLLHUP   客戶端斷開事件

epoll.unregister(文件句柄)   銷毀文件句柄

epoll.poll(timeout)  當文件句柄發生變化,則會以列表的形式主動報告給用戶進程,timeout

                     為超時時間,默認為-1,即一直等待直到文件句柄發生變化,如果指定為1

                     那么epoll每1秒匯報一次當前文件句柄的變化情況,如果無變化則返回空

epoll.fileno() 返回epoll的控制文件描述符(Return the epoll control file descriptor)

epoll.modfiy(fineno,event) fineno為文件描述符 event為事件類型  作用是修改文件描述符所對應的事件

epoll.fromfd(fileno) 從1個指定的文件描述符創建1個epoll對象

epoll.close()   關閉epoll對象的控制文件描述符
#理解版
import socket
import select

s = socket.socket()
s.bind(("127.0.0.1",1689))
s.listen()

# 創建一個epoll對象
epoll = select.epoll()

# 注冊讀就緒事件 (有數據可以讀取了)
# s.fileno()用於獲取文件描述符
epoll.register(s.fileno(),select.EPOLLIN)


# 存儲文件描述符與socket的對應關系
fd_sockets = {s.fileno():s}


while True:
    # 該函數是阻塞會直到你關注的事件發生
    # 返回值為文件描述符與發生的事件類型  是一個列表 列表中是元組  第一個是描述符 第二個是事件
    for fd,event in epoll.poll():
        print("有socket 搞事情了!")
        sock = fd_sockets[fd] # 取出對應的socket對象

        # 判斷socket是服務器還是客戶端
        if sock == s:
            # 執行對應的接收或發送
            client,addr = sock.accept()
            # 注冊客戶端的事件
            epoll.register(client.fileno(),select.EPOLLIN)
            # 將對應關系存儲到字典中
            fd_sockets[client.fileno()] = client
            print("來了一個客戶端....")

        elif event == select.EPOLLIN: #客戶端的處理
            data = sock.recv(1024)
            if not data:
                epoll.unregister(fd) # 注銷事件
                fd_sockets.pop(fd) # 從字典中刪除
                sock.close()  # 關閉socket
                continue

            print("%s 發來問候:%s" % (sock,data.decode("utf-8")))

            #將事件轉換為可寫
            epoll.modify(fd,select.EPOLLOUT)
        else:
            sock.send("我是服務器  你丫是誰?".encode("utf-8"))
            # 將事件轉換為可讀
            epoll.modify(fd, select.EPOLLIN)
#正規版
# coding:utf-8
import socket, select

server = socket.socket()
server.bind(("127.0.0.1", 1688))
server.listen(5)

msgs = []


fd_socket = {server.fileno(): server}
epoll = select.epoll()
# 注冊服務器的 寫就緒
epoll.register(server.fileno(), select.EPOLLIN)

while True:
    for fd, event in epoll.poll():
        sock = fd_socket[fd]
        print(fd, event)
        # 返回的是文件描述符 需要獲取對應socket
        if sock == server:  # 如果是服務器 就接受請求
            client, addr = server.accept()
            # 注冊客戶端寫就緒
            epoll.register(client.fileno(), select.EPOLLIN)
            # 添加對應關系
            fd_socket[client.fileno()] = client

        # 讀就緒
        elif event == select.EPOLLIN:
            data = sock.recv(2018)
            if not data:
                # 注銷事件
                epoll.unregister(fd)
                # 關閉socket
                sock.close()
                # 刪除socket對應關系
                del fd_socket[fd]
                print(" somebody fuck out...")
                continue

            print(data.decode("utf-8"))
            # 讀完數據 需要把數據發回去所以接下來更改為寫就緒=事件
            epoll.modify(fd, select.EPOLLOUT)
            #記錄數據
            msgs.append((sock,data.upper()))
        elif event == select.EPOLLOUT:
            for item in msgs[:]:
                if item[0] == sock:
                    sock.send(item[1])
                    msgs.remove(item)
            # 切換關注事件為寫就緒
            epoll.modify(fd,select.EPOLLIN)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM