TCP并发服务器 单循环服务器一次只能处理一个客户端任务的服务器。并发服务器能够同时处理多个客户端任务的服务器。TCP并发服务端1. 多进程安全、资源开销大、并发量小2. 多线程相对进程资源开销小相对并发量大3. 线程池提前创建好大量线程管理起来避免反复创建线程带来时间消耗生产者--消费者 设计模式4. IO多路复用文件 ---fd;对多个文件描述符的监测复用一个进程Linux常用IO模型阻塞IOfgets()--read()--fread()--recv()特点1. 让多个IO具有先后顺序2. 节省CPU资源头文件 #ifndef __HEAD_H__ #define __HEAD_H__ #include arpa/inet.h #include errno.h #include error.h #include netinet/in.h #include netinet/ip.h #include pthread.h #include semaphore.h #include stdio.h #include stdlib.h #include string.h #include sys/fcntl.h #include sys/socket.h #include sys/stat.h #include sys/types.h #include sys/wait.h #include unistd.h #endif写端 #include head.h int main(int argc, const char *argv[]) { mkfifo(myfifo, 0664); int fd open(myfifo, O_WRONLY); if (fd 0) { perror(open fifo error); return -1; } while (1) { write(fd, hello world, 11); sleep(1); } close(fd); return 0; }读端 #include head.h int main(int argc, const char *argv[]) { mkfifo(myfifo, 0664); int fd open(myfifo, O_RDONLY); if (fd 0) { perror(open fifo error); return -1; } char buff[1024] {0}; while (1) { fgets(buff, sizeof(buff), stdin); printf(STDIN: %s\n, buff); memset(buff, 0, sizeof(buff)); read(fd, buff, sizeof(buff)); printf(FIFO: %s\n, buff); } close(fd); return 0; }非阻塞IO特点1. 搭配轮询方式实现2. 浪费CPU资源写端 #include head.h int main(int argc, const char *argv[]) { mkfifo(myfifo, 0664); int fd open(myfifo, O_WRONLY); if (fd 0) { perror(open fifo error); return -1; } while (1) { write(fd, hello world, 11); sleep(1); } close(fd); return 0; }读端 #include head.h int main(int argc, char **argv) { mkfifo(myfifo, 0664); int fd open(myfifo, O_RDONLY); if (fd 0) { perror(open fifo error); return -1; } char buff[1024] {0}; //设置0stdin为非阻塞方式 int flag fcntl(0, F_GETFL); flag | O_NONBLOCK; // flag ~O_NONBLOCK; fcntl(0, F_SETFL, flag); while (1) { memset(buff, 0, sizeof(buff)); fgets(buff, sizeof(buff), stdin); printf(STDIN:%s\n, buff); memset(buff, 0, sizeof(buff)); read(fd, buff, sizeof(buff)); printf(FIFO:%s\n, buff); } close(fd); return 0; }信号驱动IO特点1. 效率高2. 驱动的信号个数有限应用层无法区分多个IO事件写端 #include head.h int main(int argc, const char *argv[]) { mkfifo(myfifo, 0664); int fd open(myfifo, O_WRONLY); if (fd 0) { perror(open fifo error); return -1; } while (1) { write(fd, hello world, 11); sleep(1); } close(fd); return 0; }读端 #include head.h void handler(int signum) { char buff[1024] {0}; fgets(buff, sizeof(buff), stdin); printf(STDIN:%s\n, buff); } int main(int argc, char **argv) { signal(SIGIO, handler); mkfifo(myfifo, 0664); int fd open(myfifo, O_RDONLY); if (fd 0) { perror(open fifo error); return -1; } char buff[1024] {0}; //给0stdin增加异步标志位 int flag fcntl(0, F_GETFL); flag | O_ASYNC; fcntl(0, F_SETFL, flag); //关联当前进程接收SIGIO信号 fcntl(0, F_SETOWN, getpid()); while (1) { memset(buff, 0, sizeof(buff)); read(fd, buff, sizeof(buff)); printf(FIFO:%s\n, buff); } close(fd); return 0; }IO多路复用IO多路复用的应用场景在一个进程中同时监测多个IO步骤创建文件描述符集合添加需要监测的文件描述符到集合通知内核开始监测由IO事件到达时监测返回结果select函数int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 功能:通知内核监测文件描述符中的事件 参数 nfds 集合中最大的文件描述符1 readfds读事件的文件描述符集合 writefds写事件的文件描述符集合 exceptfds其他 timeout超时时间 NULL 不设置超时时间阻塞 返回值 成功返回到达事件的个数 失败-1 void FD_CLR(int fd, fd_set *set); 功能将fd对应文件描述符清零 int FD_ISSET(int fd, fd_set *set); 功能判断fd对应的文件描述符是否被置位 void FD_SET(int fd, fd_set *set); 功能将集合表中的fd对用的文件描述符置位 void FD_ZERO(fd_set *set); 功能将集合表全部清零写端 #include head.h int main(int argc, const char *argv[]) { mkfifo(myfifo, 0664); int fd open(myfifo, O_WRONLY); if (fd 0) { perror(open fifo error); return -1; } while (1) { write(fd, hello world, 11); sleep(1); } close(fd); return 0; }读端 #include sys/select.h #include head.h int main(int argc, char **argv) { mkfifo(myfifo, 0664); int fd open(myfifo, O_RDONLY); if (fd 0) { perror(open fifo error); return -1; } //创建集合 fd_set rdfds; fd_set tmprdfds; FD_ZERO(rdfds); //添加检测的文件描述符 FD_SET(0, rdfds); int maxfd 0; FD_SET(fd, rdfds); maxfd maxfd fd ? maxfd : fd; char buff[1024] {0}; while (1) { tmprdfds rdfds; int ret select(maxfd 1, tmprdfds, NULL, NULL, NULL); if (ret 0) { perror(select error); return -1; } if (FD_ISSET(0, tmprdfds)) { memset(buff, 0, sizeof(buff)); fgets(buff, sizeof(buff), stdin); printf(STDIN:%s\n, buff); } if (FD_ISSET(fd, tmprdfds)) { memset(buff, 0, sizeof(buff)); read(fd, buff, sizeof(buff)); printf(FIFO:%s\n, buff); } } close(fd); return 0; }epoll函数int epoll_create(int size); 功能创建文件描述符集合 参数 size监测的文件描述符的最大上限 返回值 成功返回集合代表的文件描述符 失败-1 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 功能对epoll的文件描述符集合进行操作 参数 epfd文件描述符集合 op操作指令 EPOLL_CTL_ADD:添加 EPOLL_CTL_MOD:修改 EPOLL_CTL_DEL:删除 fd需要操作的文件描述符 event事件类型 返回值 成功0 失败-1 typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); 功能通知内核开始监测文件描述符集合并等待返回监测到的事件结果 参数 epfd文件描述符集合 events保存到达事件的数组 maxevents监测的事件最大个数 timeout 超时时间 -1 不设置超时 返回值 成功返回到达事件的个数 失败-1写端 #include head.h int main(int argc, const char *argv[]) { mkfifo(myfifo, 0664); int fd open(myfifo, O_WRONLY); if (fd 0) { perror(open fifo error); return -1; } while (1) { write(fd, hello world, 11); sleep(1); } close(fd); return 0; }读端 #include stdint.h #include head.h #define EPOLL_MAX_FD_CNT 2 int epoll_add_fd(int fds, int fd, uint32_t event) { struct epoll_event ev; ev.events event; ev.data.fd fd; int ret epoll_ctl(fds, EPOLL_CTL_ADD, fd, ev); if (ret 0) { perror(epoll_ctl add error); return -1; } return 0; } int epoll_del_fd(int fds, int fd) { int ret epoll_ctl(fds, EPOLL_CTL_DEL, fd, NULL); if (ret 0) { perror(epoll_ctl del error); return -1; } return 0; } int main(int argc, char **argv) { mkfifo(myfifo, 0664); int fd open(myfifo, O_RDONLY); if (fd 0) { perror(open fifo error); return -1; } //创建文件描述符集合 int fds epoll_create(EPOLL_MAX_FD_CNT); if (fds 0) { perror(epoll_create error); return -1; } //将文件描述符加入到集合 epoll_add_fd(fds, 0, EPOLLIN); epoll_add_fd(fds, fd, EPOLLIN); char buff[1024] {0}; struct epoll_event evs[EPOLL_MAX_FD_CNT]; while (1) { //通知内核开始监测 int ret epoll_wait(fds, evs, EPOLL_MAX_FD_CNT, -1); if (ret 0) { perror(epoll_wait error); return -1; } for (int i 0; i ret; i) { if (0 evs[i].data.fd) { memset(buff, 0, sizeof(buff)); fgets(buff, sizeof(buff), stdin); printf(STDIN:%s\n, buff); } else if (fd evs[i].data.fd) { memset(buff, 0, sizeof(buff)); ssize_t size read(fd, buff, sizeof(buff)); if (size 0) { perror(read fifo error); epoll_del_fd(fds, evs[i].data.fd); close(evs[i].data.fd); continue; } else if (0 size) { epoll_del_fd(fds, evs[i].data.fd); close(evs[i].data.fd); continue; } printf(FIFO:%s\n, buff); } } } close(fd); return 0; }select和epoll的区别select函数select使用数组位图保存文件描述符集合最多允许监测1024个文件描述符select将集合表创建在应用层需要应用层和内核层的反复数据拷贝select需要遍历寻找到达的IO事件效率低select只能工作在水平触发模式低速模式不能工作在边沿触发模式高速模式poll函数poll使用链表保存文件描述符集合理论上没有文件描述符限制poll将集合表创建在应用层需要应用层和内核层的反复数据拷贝poll需要遍历寻找到达的IO事件效率低poll只能工作在水平触发模式低速模式不能工作在边沿触发模式高速模式epoll函数epoll使用树形结构保存文件描述符集合红黑树epoll将结合表直接创建在内核层避免应用层和内核层的反复数据拷贝epoll直接返回到达的事件不需要应用层遍历查找epoll可以工作在水平触发模式低速模式也可以工作在边沿触发模式高速模式