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()