linux高性能服务器编程---第六章高级IO函数 (3)

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");
        }
    }
}

 

posted @ 2022-05-22 15:58  马梦佳  阅读(18)  评论(0编辑  收藏  举报