epoll案例


C语言版

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define MAX_EVENTS 10
#define BUFFER_SIZE 1024

int main() {
  int server_fd, client_fd, epoll_fd, nfds, n;
  struct epoll_event ev, events[MAX_EVENTS];
  struct sockaddr_in server_addr, client_addr;
  socklen_t client_addr_len = sizeof(client_addr);

  // 创建 TCP 套接字
  server_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (server_fd == -1) {
    perror("socket");
    exit(EXIT_FAILURE);
  }

  // 设置地址和端口
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(8080);

  // 绑定套接字到地址和端口
  if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) ==
      -1) {
    perror("bind");
    exit(EXIT_FAILURE);
  }

  // 监听套接字
  if (listen(server_fd, 5) == -1) {
    perror("listen");
    exit(EXIT_FAILURE);
  }

  // 创建 epoll 对象
  epoll_fd = epoll_create1(0);
  if (epoll_fd == -1) {
    perror("epoll_create");
    exit(EXIT_FAILURE);
  }

  // 注册要监听的事件类型
  ev.events = EPOLLIN;
  ev.data.fd = server_fd;
  if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) {
    perror("epoll_ctl: server_fd");
    exit(EXIT_FAILURE);
  }

  while (1) {
    // 等待事件的发生
    nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
    if (nfds == -1) {
      perror("epoll_wait");
      exit(EXIT_FAILURE);
    }

    // 处理发生的事件
    for (n = 0; n < nfds; ++n) {
      if (events[n].data.fd == server_fd) { // 有新的连接
        client_fd = accept(server_fd, (struct sockaddr *)&client_addr,
                           &client_addr_len);
        if (client_fd == -1) {
          perror("accept");
          exit(EXIT_FAILURE);
        }
        printf("New client connected: %s\n", inet_ntoa(client_addr.sin_addr));

        // 将新连接的套接字添加到 epoll 对象中进行监听
        ev.events = EPOLLIN | EPOLLET; // 采用边缘触发模式
        ev.data.fd = client_fd;
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) {
          perror("epoll_ctl: client_fd");
          exit(EXIT_FAILURE);
        }
      } else { // 有数据到达
        int fd = events[n].data.fd;
        char buffer[BUFFER_SIZE];
        int bytes_read = recv(fd, buffer, BUFFER_SIZE, 0);
        if (bytes_read == -1) {
          perror("recv");
          exit(EXIT_FAILURE);
        }
        if (bytes_read == 0) { // 客户端关闭了连接
          printf("Client disconnected\n");
          close(fd);
        } else {
          buffer[bytes_read] = '\0';
          printf("Received %d bytes of data from client: %s", bytes_read,
                 buffer);
          // 将数据发送回客户端
          if (send(fd, buffer, bytes_read, 0) == -1) {
            perror("send");
            exit(EXIT_FAILURE);
          }
        }
      }
    }
  }

  close(server_fd);
  return 0;
}

Python版

import select
import socket

# 创建 TCP 服务器套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('localhost', 8000))
server_socket.listen(5)
server_socket.setblocking(0)

# 创建 epoll 对象
epoll = select.epoll()
epoll.register(server_socket.fileno(), select.EPOLLIN)

try:
    connections = {}
    requests = {}
    responses = {}

    while True:
        events = epoll.poll(1)
        for fileno, event in events:
            # 处理新的连接
            if fileno == server_socket.fileno():
                connection, address = server_socket.accept()
                connection.setblocking(0)
                epoll.register(connection.fileno(), select.EPOLLIN)
                connections[connection.fileno()] = connection
                requests[connection.fileno()] = b''
            # 处理现有连接的数据读取事件
            elif event & select.EPOLLIN:
                data = connections[fileno].recv(1024)
                if data:
                    requests[fileno] += data
                    epoll.modify(fileno, select.EPOLLOUT)
                else:
                    epoll.unregister(fileno)
                    connections[fileno].close()
                    del connections[fileno]
                    del requests[fileno]
                    del responses[fileno]
            # 处理现有连接的数据写入事件
            elif event & select.EPOLLOUT:
                response = b'HTTP/1.1 200 OK\r\n\r\nHello, World!'
                connections[fileno].send(response)
                epoll.modify(fileno, select.EPOLLET)
            # 处理连接的错误和关闭事件
            elif event & select.EPOLLHUP:
                epoll.unregister(fileno)
                connections[fileno].close()
                del connections[fileno]
                del requests[fileno]
                del responses[fileno]

except KeyboardInterrupt:
    pass

finally:
    epoll.unregister(server_socket.fileno())
    epoll.close()
    server_socket.close()