splice函数 用于在两个文件名描述符之间移动数据, 0拷贝操作
#include <fcntl.h> // fd_in 为文件描述符, 如果为管道文件描述符则 off_in必须为NULL, 否则为读取开始偏移位置 // len为指定移动的数据长度, flags参数控制数据如何移动. // - SPLICE_F_NONBLOCK 非阻塞splice操作, 但会受文件描述符自身的阻塞 // - SPLICE_F_MORE 给内核一个提示, 后续的splice调用将读取更多的数据??????? ssize_t splice(int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags); // 使用splice函数 实现echo服务器 int main(int argc, char* argv[]) { if (argc <= 2) { printf("the parmerters is wrong\n"); exit(errno); } char *ip = argv[1]; int port = atoi(argv[2]); printf("the port is %d the ip is %s\n", port, ip); int sockfd = socket(PF_INET, SOCK_STREAM, 0); assert(sockfd >= 0); struct sockaddr_in address{}; address.sin_family = AF_INET; address.sin_port = htons(port); inet_pton(AF_INET, ip, &address.sin_addr); int ret = bind(sockfd, (sockaddr*)&address, sizeof(address)); assert(ret != -1); ret = listen(sockfd, 5); int clientfd{}; sockaddr_in client_address{}; socklen_t client_addrlen = sizeof(client_address); clientfd = accept(sockfd, (sockaddr*)&client_address, &client_addrlen); if (clientfd < 0) { printf("accept error\n"); } else { printf("a new connection from %s:%d success\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port)); int fds[2]; pipe(fds); ret = splice(clientfd, nullptr, fds[1], nullptr, 32768, SPLICE_F_MORE); assert(ret != -1); ret = splice(fds[0], nullptr, clientfd, nullptr, 32768, SPLICE_F_MORE); assert(ret != -1); close(clientfd); } close(sockfd); exit(0); }
select 函数 select函数在第二个参数列表 可读的时候返回 或者是等到了规定的时间返回
返回之后 第二个参数指向fdset的集合 被修改为可读的fd列表 这就需要每次返回后都更新 fdset集合
返回后 此函数的返回值为可读的fd数量, 遍历fdset集合 同时使用FD_ISSET判断fdset[i] 是否在其中 然后判断此fd是否为listenfd 如果是则接受新的连接 如果不是说明是已经接受的其他fd 判断是有数据可读 还是此连接断开
#include <fcntl.h> // maxfdp 最大数 FD_SETSIZE // struct fd_set 一个集合,可以存储多个文件描述符 // - FD_ZERO(&fd_set) 清空 -FD_SET(fd, &fd_set) 放入fd FD_CLR(fd, &fd_set)从其中清除fd // - FD_ISSET(fd, &fd_set) 判断是否在其中 // readfds 需要监视的文件描述符读变化, 其中的文件描述符可读的时候返回 // writefds 需要监视的文件描述符写变化, 其中的文件描述符可写的时候返回 // errorfds 错误 // timeout 传入NULL为阻塞, 设置为0秒0微秒则变为非阻塞函数 // 返回值 负值为错误 等待超时说明文件无变化返回0 有变化返回正值 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout); #define exit_if(r, ...) \ { \ if (r) \ { \ printf(__VA_ARGS__); \ printf("errno no: %d, error msg is %s", errno, strerror(errno)); \ exit(1); \ } \ } \ int main(int argc, char* argv[]) { int keyboard_fd = open("/dev/tty", O_RDONLY | O_NONBLOCK); exit_if(keyboard_fd < 0, "open keyboard fd error\n"); fd_set readfd; char recv_buffer = 0; while (true) { FD_ZERO(&readfd); FD_SET(0, &readfd); timeval timeout {5, 0}; int ret = select(keyboard_fd + 1, &readfd, nullptr, nullptr, &timeout); exit_if(ret == -1, "select error\n"); if (ret > 0) { if (FD_ISSET(keyboard_fd, &readfd)) { recv_buffer = 0; read(keyboard_fd, &recv_buffer, 1); if ('\n' == recv_buffer) { continue; } if ('q' == recv_buffer) { break; } printf("the input is %c\n", recv_buffer); } } if (ret == 0) { printf("timeout\n"); } } }