由于多线程或者多进程会消耗资源
线程或进程调度消耗CPU资源
出现了I/O多路复用,I/O多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,Linux下实现I/O多路系统调用主要有slelect, poll和epoll
创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,一个是需要检 测的文件描述符的信息(红黑树),红黑树的查找删除修改的效率很高,还有一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向 链表)。
服务器端
#include
#include
#include
#include
#include
#include
int main() {
// 创建socket
int lfd = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in saddr;
saddr.sin_port = htons(9999);
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
// 绑定
bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
// 监听
listen(lfd, 8);
//调用epoll_create()创建一个epoll实例
int epfd = epoll_create(100);
//将监听的文件描述符相关的检测信息添加到epoll实例中
struct epoll_event epev;
epev.data.fd = lfd;
//常见的EPOll事件 EPOLLIN-读事件 EPOLLOUT-写事件 EPOLLERR-错误事件
epev.events = EPOLLIN;
//lfd表示要检测的文件描述符
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);
//一个用于保存变化的数组
struct epoll_event epevs[1024];
while(1) {
//epfd:epoll实例对应的文件描述符
//epevs,表示传出参数,保存了发送变化的文件描述符的信息
//epoll_wait用于检测变化了的个数
int ret = epoll_wait(epfd, epevs, 1024, -1);
if(ret == -1) {
perror("epoll_wait");
exit(-1);
}
printf("ret %d\n", ret);
for (int i = 0; i < ret; i++) {
int curfd = epevs[i].data.fd;
if (curfd == lfd) {
//监听的文件描述符有数据达到,表示有客户端连接
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
int cfd = accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t*)&len);
epev.events = EPOLLIN;
epev.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD, cfd, &epev);
}else {
//为了排除写事件
if (epevs[i].events & EPOLLOUT) continue;
// 有数据到达,需要通信,有数据到达和有客户端连接的含义不一样
char buf[1024] = {0};
int len = read(curfd, buf, sizeof(buf));
if(len == -1) {
perror("read");
exit(-1);
} else if(len == 0) {
printf("client closed...\n");
epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
close(curfd);
} else if(len > 0) {
printf("read buf = %s\n", buf);
write(curfd, buf, strlen(buf) + 1);
}
}
}
}
close(lfd);
close(epfd);
return 0;
}
客户端
#include
#include
#include
#include
#include
int main() {
// 创建socket
int fd = socket(PF_INET, SOCK_STREAM, 0);
if(fd == -1) {
perror("socket");
return -1;
}
struct sockaddr_in seraddr;
inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(9999);
// 连接服务器
int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if(ret == -1){
perror("connect");
return -1;
}
int num = 0;
while(1) {
char sendBuf[1024] = {0};
sprintf(sendBuf, "send data %d", num++);
write(fd, sendBuf, strlen(sendBuf) + 1);
// 接收
int len = read(fd, sendBuf, sizeof(sendBuf));
if(len == -1) {
perror("read");
return -1;
}else if(len > 0) {
printf("read buf = %s\n", sendBuf);
} else {
printf("服务器已经断开连接...\n");
break;
}
// sleep(1);
usleep(1000);
}
close(fd);
return 0;
}