web服务器--并发web服务器实现-epoll

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。

epoll 是当今 linux 系统采用的方式,一般服务器都是用 epoll 来实现的。epoll 是一种方案,这种方案不使用多进程多线程多协程,而是使用单进程单线程来实现高并发。那我们之前写的单进程单线程非阻塞的程序和epoll有什么区别呢?

我们之前写的程序是建一个列表存储套接字,然后遍历,这种方式叫轮询。明显比较慢。
epoll :是谁收到谁举手,这种方式叫事件通知。

在这里插入图片描述
蓝色表示共享内存,绿色表示存储列表,黄色套接字,白色监听套接字。
程序来实现:

import socket
import re
import select

def service_client(new_client_sock,requst):
    '''为这个客户服务'''

    # 转为列表
    requst_lines = requst.splitlines()

    # 寻找文件名  GET /index.html HTTP/1.1
    # 开头有:GET POST PUT DEL 不一定是GET 所以应该不能用GET匹配。
    file_name = ''
    ret = re.match(r'[^/]+(/[^ ]*)',requst_lines[0])
    if ret:
        file_name = ret.group(1)
        if file_name == '/ ':
            file_name = '/index.html'

    # 2.3 准备body,且这里准备的body不能和之前的字符串一样,直接相加,而是要单独发送。
    # 找到相对应的.html文件
    try:
        f = open('./html'+file_name,'rb')
    except:
        response = 'HTTP/1.1 404 NOT FOUND\r\n'
        response += '\r\n'
        response += '---file not found--'
    else:
        html_content = f.read()
        f.close()

        # 2.返回数据给浏览器,http 格式
        response_body = html_content

        response_header = 'HTTP/1.1 200 OK\r\n'
        response_header += 'Content-Length:%d\r\n' % len(response_body)
        response_header += '\r\n'

        response = response_header.encode('utf-8') + response_body

        # 2.4 发送数据给浏览器
        new_client_sock.send((response))

    # 3.长连接不能使用close关闭套接字,那么我们怎么知道数据已经发完了呢?
    # 在 header 中加入发送数据的长度 Content-Length:
    # new_client_sock.close()


def main():
    '''用来完成整体的控制'''

    # 1.创建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2.绑定
    tcp_socket.bind(('', 7890))

    # 3.变为监听套接字
    tcp_socket.listen(128)
    tcp_socket.setblocking(False)  # 将套接字变为非堵塞

    # 创建一个epoll对象
    epl = select.epoll()

    # 将监听套接字对应的 fd 注册到 epoll中。
    epl.register(tcp_socket.fileno(), select.EPOLLIN)
    fd_event_dict = dict()

    while True:

        fd_event_list = epl.poll()  # 默认会堵塞,直到os 检测到数据到来,通过事件通知的方式告诉这个程序,此时才会解堵塞

        # [(fd, event),(套接字对应的文字描述符,这个文件描述符到底是什么事件,例如:可以调用recv接收等)]
        for fd,enent in fd_event_list:

            # 4.等待客户端的链接
            if fd == tcp_socket.fileno():
                new_client_sock, client_addr = tcp_socket.accept()
                epl.register(new_client_sock.fileno(), select.EPOLLIN)
                fd_event_dict[new_client_sock.fileno()] = new_client_sock
            elif event == select.EPOLLIN:
                # 判断已经链接的客户端是否有数据发送过来
                recv_data = fd_event_dict[fd].recv(1024).decode('utf-8')

                if recv_data:
                    service_client(fd_event_dict[fd],recv_data)
                else:
                    fd_event_dict[fd].close()
                    epl.unregister(fd)
                    del fd_event_dict[fd]

    # 4.关闭监听套接字
    tcp_socket.close()


if __name__ == '__main__':
    main()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用C语言编写Web服务器可以使用I/O多路复用技术提高服务器的性能和并发能力。以下是基于Linux下的I/O多路复用的Web服务器实现步骤: 1. 创建socket并绑定IP地址和端口号 2. 将socket设置为非阻塞模式 3. 创建epoll句柄,并将socket加入epoll监听列表中 4. 进入事件循环,等待客户端连接请求 5. 当有客户端连接请求到来时,使用accept函数接受连接,并将新连接加入epoll监听列表中 6. 当有读写事件到来时,使用recv和send函数进行读写操作 7. 关闭连接,从epoll监听列表中删除连接 下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/epoll.h> #define MAX_EVENTS 1024 #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { struct sockaddr_in address; int listenfd, connfd, epollfd, nfds, n, i; char buffer[BUFFER_SIZE]; struct epoll_event ev, events[MAX_EVENTS]; if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_ANY); address.sin_port = htons(8080); if (bind(listenfd, (struct sockaddr *)&address, sizeof(address)) == -1) { perror("bind"); exit(1); } if (listen(listenfd, 5) == -1) { perror("listen"); exit(1); } if ((epollfd = epoll_create(1)) == -1) { perror("epoll_create"); exit(1); } ev.events = EPOLLIN; ev.data.fd = listenfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) { perror("epoll_ctl"); exit(1); } while (1) { nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); exit(1); } for (i = 0; i < nfds; i++) { if (events[i].data.fd == listenfd) { if ((connfd = accept(listenfd, (struct sockaddr *)NULL, NULL)) == -1) { perror("accept"); exit(1); } ev.events = EPOLLIN | EPOLLET; ev.data.fd = connfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) == -1) { perror("epoll_ctl"); exit(1); } } else { if ((n = recv(events[i].data.fd, buffer, BUFFER_SIZE, 0)) <= 0) { close(events[i].data.fd); continue; } buffer[n] = '\0'; printf("Received message: %s\n", buffer); if (send(events[i].data.fd, buffer, n, 0) == -1) { perror("send"); exit(1); } } } } close(listenfd); close(epollfd); return 0; } ``` 此示例代码只是一个简单的Web服务器,可以作为你自己的Web服务器的基础框架。如果需要更完善的功能,你需要进一步完善代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值