epoll 的理解與應用
select 缺點:
- 效率太低
- 每次向操作系統傳輸對象信息
epoll的優點:
- 無編寫針對文件描述符的循環語句
- 無需每次傳遞監控對象信息
#include <sys/epoll.h>
int epoll_create(int size) 創建保存epoll文件描述符的空間
size 實例大小 可忽略
創建於套接字相同的資源,也是文件描述符返回
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 等待發生變化
失敗返回 -1
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event): 向空間註冊並註銷文件描述符
成功0 失敗-1
epfd epoll 例程的文件描述符
op 指定對象的操作
fd 對象的文件描述符
event 事件類型
struct epoll_event {
__uint32_t events; //發生的事件情況
epoll_data_t data;
}
typedef union epoll_data {
void *ptr;
int fd;
__unint32_t u32;
__unint64_t u64;
}epoll_dada_t;
參數:
EPOLL_CTL_ADD 註冊
。。。。。_DEL 刪除
。。。。。_MOD 更改
具體服務端代碼如下:
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/epoll.h>
#define BUF_SIZE 100
#define SIZE 50
void error(char *message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char *argv[]) {
int ser_sock, cli_sock;
struct sockaddr_in se_adr, cl_adr;
socklen_t adr_sz;
int str_len, i;
char buf[4];
struct epoll_event *ep_events;
struct epoll_event event;
int epfd, event_cnt;
ser_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&se_adr, 0, sizeof(se_adr));
se_adr.sin_family = AF_INET;
se_adr.sin_addr.s_addr = htonl(INADDR_ANY);
se_adr.sin_port = htons(atoi(argv[1]));
if (bind(ser_sock, (struct sockaddr*)&se_adr, sizeof(se_adr)) == -1)
error("bind() error");
listen(ser_sock, 5);
epfd = epoll_create(SIZE);
ep_events = malloc(sizeof(struct epoll_event)*SIZE);
event.events = EPOLLIN;
event.data.fd = ser_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, ser_sock, &event);
while (1) {
event_cnt = epoll_wait(epfd, ep_events, SIZE, -1);
if (event_cnt == -1)
error("wait error");
puts("wait......");
for (int i = 0; i < event_cnt; ++i) {
if (ep_events[i].data.fd == ser_sock) {
adr_sz = sizeof(cl_adr);
cli_sock = accept(ser_sock, (struct sockaddr*) &cl_adr, &adr_sz);
event.events = EPOLLIN;
event.data.fd = cli_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, cli_sock, &event);
printf("new connected ......\n");
}
else {
str_len = read(ep_events[i].data.fd, buf, SIZE);
if (str_len == 0) {
epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
close(ep_events[i].data.fd);
printf("disconnect %d\n", ep_events[i].data.fd);
}
else {
write(ep_events[i].data.fd, buf, str_len);
}
}
}
}
close(ser_sock);
close(epfd);
return 0;
}
1. 條件觸發與邊緣觸發
邊緣觸發 是指每當狀態變化時發生一個io事件;
條件觸發 是隻要滿足條件就發生一個io事件
1.1 條件觸發
就是在每當收到客戶端數據都會註冊事件,在輸入緩存區還剩的情況下會一直註冊。
select 是條件觸發
上述的服務端就屬於條件觸發
1.2 邊緣觸發
當客戶端接收事件時,只註冊一次。
- 通過errno變量驗證錯誤原因
- 爲完成非阻塞I/o,更改套接字特性
#include <fcntl.h>
int fcntl(int filedes, int cmd,.....);
->成功返回cmd相關參數, 失敗返回-1
filedes 文件描述符
cmd 表示函數調用的目的
修改爲非阻塞模式
int flag = fcntl(fd, F_GETEL, 0);
fcntl(fd, F_SETFL, flag|O_NONBLOCK);
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#define BUF_SIZE 100
#define SIZE 50
void error(char *message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
void setmode(int fd) {
int flage = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flage|O_NONBLOCK);
}
int main(int argc, char *argv[]) {
int ser_sock, cli_sock;
struct sockaddr_in se_adr, cl_adr;
socklen_t adr_sz;
int str_len, i;
char buf[4];
struct epoll_event *ep_events;
struct epoll_event event;
int epfd, event_cnt;
ser_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&se_adr, 0, sizeof(se_adr));
se_adr.sin_family = AF_INET;
se_adr.sin_addr.s_addr = htonl(INADDR_ANY);
se_adr.sin_port = htons(atoi(argv[1]));
if (bind(ser_sock, (struct sockaddr*)&se_adr, sizeof(se_adr)) == -1)
error("bind() error");
listen(ser_sock, 5);
epfd = epoll_create(SIZE);
ep_events = malloc(sizeof(struct epoll_event)*SIZE);
setmode(ser_sock);
event.events = EPOLLIN;
event.data.fd = ser_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, ser_sock, &event);
while (1) {
event_cnt = epoll_wait(epfd, ep_events, SIZE, -1);
if (event_cnt == -1)
error("wait error");
puts("wait......");
for (int i = 0; i < event_cnt; ++i) {
if (ep_events[i].data.fd == ser_sock) {
adr_sz = sizeof(cl_adr);
cli_sock = accept(ser_sock, (struct sockaddr*) &cl_adr, &adr_sz);
event.events = EPOLLIN|EPOLLET;
event.data.fd = cli_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, cli_sock, &event);
printf("new connected ......\n");
}
else {
while (1) {
str_len = read(ep_events[i].data.fd, buf, SIZE);
if (str_len == 0) {
epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
close(ep_events[i].data.fd);
printf("disconnect %d\n", ep_events[i].data.fd);
break;
}
else if (str_len < 0){
if (errno == EAGAIN)
break;
}
else {
write(ep_events[i].data.fd, buf, str_len);
}
}
}
}
}
close(ser_sock);
close(epfd);
return 0;
}