服务器epoll多路复用

本文详细探讨了服务器中epoll的多路复用技术,阐述了其工作原理,包括如何通过epoll_ctl操作句柄,以及epoll_wait如何实现高效的数据读写。epoll相比传统的select和poll,具有更高的性能和可扩展性,尤其在高并发场景下,它能显著提升服务器的处理能力。
摘要由CSDN通过智能技术生成
import socket, sys, time, re, select


class WSGIServer(object):
    def __init__(self):
        # 创建套接字
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 地址复用
        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定地址和端端口
        self.s.bind(("", 8080))
        # 主动连接改为被动监听
        self.s.listen(128)
        # 创建epoll对象
        self.epoll = select.epoll()
        # 将tcp服务器套接字加入到epoll中监听
        self.epoll.register(self.s.fileno(), select.EPOLLIN|select.EPOLLET)
        
        # 创建添加的fd对应的套接字
        self.fdSocket = dict()

    def runServer(self):
        while True:
            # 扫描中epoll
            epollList = self.epoll.poll()

            # 对事件进行判断
            for fd, event in epollList:
                # 判断服务器套接字可以接受数据,就进行accept
                if fd == self.s.fileno():
                    # 接受客户端的数据和地址端口
                    newSocket, _ = self.s.accept()
                    # 向epoll中注册newsocketde可读事件
                    self.epoll.register(newSocket.fileno(), select.EPOLLIN|select.EPOLLET)
                    # 记录这个信息
                    self.fdSocket[newSocket.fileno()] = newSocket
                
                # 判断数据是可读的
                elif event == select.EPOLLIN:
                    # 接受数据
                    request = self.fdSocket[fd].recv(1024).decode("utf-8")
                    # 判断数据存在
                    if request:
                        # 处理存在的数据
                        self.dealWithRequest(request, self.fdSocket[fd])
                    else:
                        # 关闭数据
                        self.epoll.unregister(fd)
                        # 关闭字典中存储的数据
                        self.fdSocket[fd].close()
                        # 删除连接
                        del self.fdSocket[fd]
    
    def dealWithRequest(self, request, newSocket):
        # 数据不存在直接返回
        if not request:
            return

        # 对key进行行切片
        requestList = request.splitlines()
        # 列出行表,并进行遍历
        for i, line in enumerate(requestList):
            print(i, line)

        # 正则匹配第一行
        ret = re.match(r"([^/]*)([^ ]+)", requestList[0])
        # 判断数据
        if ret:
            # print("正则提取数据:", ret.group(1))
            # print("正则提取数据:", ret.group(2))
            
            # 判断网站根目录 
            fileName = ret.group(2)
            if fileName == "/":
                fileName = "/index.html"

            # 异常处理
            try:
                # 打开文件
                f = open(self.documentsRoot + fileName, "rb")
            except:

                # 打开文件失败就返回以下代码
                responseBody = "file not found, 请输入正确的url"
                responseHeader = "HTTP/1.1 404 not found\r\n"
                responseHeader += "Content-Type: text/html; charset=utf-8\r\n"
                responseHeader += "Content-Length: %d\r\n" % len(responseBody)
                responseHeader += "\r\n"

                response = responseHeader + responseBody
                newSocket.send(response.encode("utf-8"))
            
            else:
                # 成功打开文件就执行以下代码
                # 读取数据
                content = f.read()
                关闭文件
                f.close()
                
                # 读取的数据赋值给请求体
                responseBody = content

                responseHeader = "HTTP/1.1 200 Ok\r\n"
                responseHeader += "Content-Length: %d\r\n" % len(responseBody) + "\r\n"
                
                # 拼接请求头和请求提
                response = responseHeader + responseBody
                
                # 向客户端发送数据
                newSocket.send(response.encode("utf-8"))

def main():
    http = WSGIServer()
    http.runServer()


if __name__ == "__main__":
    main()
EPOLLIN (可读)
EPOLLOUT (可写).
EPOLLET (ET模式

epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式。
LT模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll时,会再次响应应用程序并通知此事件。
ET模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll时,不会再次响应应用程序并通知此事件。


I/O 多路复用的特点:
通过一种机制使一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,epoll()函数就可以返回。 所以, IO多路复用,本质上不会有并发的功能,因为任何时候还是只有一个进程或线程进行工作,它之所以能提高效率是因为select\epoll 把进来的socket放到他们的 '监视' 列表里面,当任何socket有可读可写数据立马处理,那如果select\epoll 手里同时检测着很多socket, 一有动静马上返回给进程处理,总比一个一个socket过来,阻塞等待,处理高效率。


当然也可以多线程/多进程方式,一个连接过来开一个进程/线程处理,这样消耗的内存和进程切换页会耗掉更多的系统资源。 所以我们可以结合IO多路复用和多进程/多线程 来高性能并发,IO复用负责提高接受socket的通知效率,收到请求后,交给进程池/线程池来处理逻辑

参考资料:http://blog.csdn.net/xiajun07061225/article/details/9250579
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值