頭文件:#include <sys/epoll.h>
int epoll_create(int size):創建 epoll 實例,返回句柄。size 為建議監聽上限(內核 2.6.8+ 忽略此值)。int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event):註冊/修改/刪除監聽事件。
op:EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL。event.events:事件掩碼(見下)。event.data.fd:關聯的文件描述符。int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout):等待事件發生,返回就緒事件數。timeout:-1 永久阻塞,0 立即返回,>0 毫秒超時。close(epfd):關閉 epoll 句柄。typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t;
struct epoll_event { uint32_t events; epoll_data_t data; };
events 宏:
EPOLLIN:可讀(含對端正常關閉)。EPOLLOUT:可寫。EPOLLPRI:帶外數據可讀。EPOLLERR:錯誤。EPOLLHUP:掛起。EPOLLET:邊緣觸發(ET),默認水平觸發(LT)。EPOLLONESHOT:單次觸發,需重新註冊。#include <sys/socket.h>, <sys/epoll.h>, <netinet/in.h>, <arpa/inet.h>,
<fcntl.h>, <unistd.h>, <stdio.h>, <errno.h>, <string.h>
#define MAXLINE 5, OPEN_MAX 100, LISTENQ 20, SERV_PORT 5000, INFTIM 1000
void setnonblocking(int sock) {
int opts = fcntl(sock, F_GETFL);
if (opts < 0) { perror("fcntl GETFL"); return; }
opts |= O_NONBLOCK;
if (fcntl(sock, F_SETFL, opts) < 0) { perror("fcntl SETFL"); return; }
}
void CloseAndDisable(int sockid, struct epoll_event *ee) {
close(sockid); ee->data.fd = -1;
}
int main() {
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serveraddr = {0};
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERV_PORT);
setnonblocking(listenfd);
bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ);
int epfd = epoll_create(256);
struct epoll_event ev, events[20];
ev.data.fd = listenfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
for (;;) {
int nfds = epoll_wait(epfd, events, 20, -1);
printf("\nepoll_wait returns %d\n", nfds);
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == listenfd) { // 新連接
struct sockaddr_in cliaddr; socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
if (connfd < 0) { perror("accept"); continue; }
printf("accept from %s\n", inet_ntoa(cliaddr.sin_addr));
setnonblocking(connfd);
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
}
else if (events[i].events & EPOLLIN) { // 讀事件
int sockfd = events[i].data.fd;
if (sockfd < 0) continue;
char line[MAXLINE]; int count = 0, recvNum; bool bReadOk = false;
while (1) {
recvNum = recv(sockfd, line + count, MAXLINE, 0);
if (recvNum < 0) {
if (errno == EAGAIN) { bReadOk = true; break; } // 非阻塞讀完
else if (errno == ECONNRESET) { CloseAndDisable(sockfd, &events[i]); break; }
else if (errno == EINTR) continue;
else { CloseAndDisable(sockfd, &events[i]); break; }
}
else if (recvNum == 0) { // 對端關閉
CloseAndDisable(sockfd, &events[i]); break;
}
count += recvNum;
if (recvNum == MAXLINE) continue; // 可能還有數據
else { bReadOk = true; break; }
}
if (bReadOk) {
line[count] = '\0';
printf("read: %s", line);
ev.data.fd = sockfd;
ev.events = EPOLLOUT | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
else if (events[i].events & EPOLLOUT) { // 寫事件
int sockfd = events[i].data.fd;
const char *str = "hello from epoll : long string may be cut\n";
int len = strlen(str), count = 0, written; bool bWritten = false;
while (1) {
written = send(sockfd, str + count, len - count, 0);
if (written == -1) {
if (errno == EAGAIN) { bWritten = true; break; }
else if (errno == ECONNRESET) { CloseAndDisable(sockfd, &events[i]); break; }
else if (errno == EINTR) continue;
else { CloseAndDisable(sockfd, &events[i]); break; }
}
else if (written == 0) { CloseAndDisable(sockfd, &events[i]); break; }
count += written;
if (count < len) continue;
else { bWritten = true; break; }
}
if (bWritten) {
ev.data.fd = sockfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
}
}
return 0;
}
#include <sys/socket.h>, <netinet/in.h>, <arpa/inet.h>, <unistd.h>,
<stdio.h>, <string.h>, <stdlib.h>, <pthread.h>
#define MAXLINE 100, SERV_PORT 5000
int sockfd; char recvline[MAXLINE+1]; pthread_t tid;
void *recv_thread(void *arg) {
int n;
while ((n = recv(sockfd, recvline, 30, 0)) > 0) {
recvline[n] = '\0'; printf("%s", recvline);
}
return NULL;
}
int main(int argc, char **argv) {
if (argc != 2) { printf("usage: %s \n", argv[0]); return 0; }
struct sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { perror("inet_pton"); return 0; }
sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
pthread_create(&tid, NULL, recv_thread, NULL);
char input[100];
while (fgets(input, 100, stdin) != EOF) write(sockfd, input, strlen(input));
return 0;
}
EAGAIN。recv 返回 0 表示對端發送 FIN(正常關閉),ECONNRESET 表示收到 RST(異常斷開)。EAGAIN 視為發送緩衝區滿,待下次 EPOLLOUT 繼續。EPOLL_CTL_MOD 在讀/寫間轉換。