Advertisement

2024年C C++最新数据的IO和复用,2024年最新2024华为C C++面试真题

阅读量:

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

writev()函数是用于发送数据的函数,它可以一次性发送多个缓冲区的数据。writev()函数的参数如下:

复制代码
    ssize\_t writev(int fd, const struct iovec \*iov, int iovcnt);
    
    
    
      
      
    

其中,fd是文件描述符,iov是一个iovec结构体数组,iovcnt是数组中元素的个数。iovec结构体定义如下:

复制代码
    struct iovec {
    void \*iov_base; /\* Starting address \*/
    size\_t iov_len; /\* Number of bytes to transfer \*/
    };
    
    
    
      
      
      
      
      
    

writev()函数将把iovec数组中所有缓冲区的数据合并到一个缓冲区中,并将它们一次性发送出去。
在调用writev()函数的时候必须指定iovec的iov_base的长度,将值放到成员iov_len中。参数vector指向一块结构vector的内存,大小由count指定。

当我们需要向多个缓冲区中写入数据时,可以使用writev()函数。下面是一个使用writev()函数的例子:

复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/uio.h>
    #include <unistd.h>
    
    int main() {
    char buf1[10] = "hello", buf2[20] = "world", buf3[30] = "!";
    struct iovec iov[3];
    ssize\_t nwritten;
    
    iov[0].iov_base = buf1;
    iov[0].iov_len = strlen(buf1);
    iov[1].iov_base = buf2;
    iov[1].iov_len = strlen(buf2);
    iov[2].iov_base = buf3;
    iov[2].iov_len = strlen(buf3);
    
    nwritten = writev(STDOUT_FILENO, iov, 3);
    if (nwritten == -1) {
        perror("writev");
        exit(EXIT_FAILURE);
    }
    
    printf("Wrote %ld bytes\n", (long) nwritten);
    
    exit(EXIT_SUCCESS);
    }
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

1.5 使用recvmsg()函数接收数据

recvmsg()函数是用于接收数据的函数,它可以一次性接收多个缓冲区的数据。recvmsg()函数的参数如下:

复制代码
    ssize\_t recvmsg(int sockfd, struct msghdr \*msg, int flags);
    
    
    
      
      
    

其中,sockfd是文件描述符,msg是一个msghdr结构体,flags是标志位。msghdr结构体定义如下:

复制代码
    struct msghdr {
    void         \*msg_name;       /\* optional address \*/
    socklen\_t     msg_namelen;    /\* size of address \*/
    struct iovec \*msg_iov;        /\* scatter/gather array \*/
    size\_t        msg_iovlen;     /\* # elements in msg\_iov \*/
    void         \*msg_control;    /\* ancillary data, see below \*/
    size\_t        msg_controllen; /\* ancillary data buffer len \*/
    int           msg_flags;      /\* flags on received message \*/
    };
    
    
    
      
      
      
      
      
      
      
      
      
      
    

recvmsg()函数将把多个缓冲区中的数据合并到一个缓冲区中,并将它们一次性接收出去。

  • 成员msg_name表示源地址,即为一个指向struct sockaddr的指针,当套接字还没有连接的时候有效。
  • 成员msg_namelen表示msg_name指向结构的长度。
  • 成员msg_iov与函数readv()中的含义一致。
  • 成员msg_iovlen表示msg_iov缓冲区的字节数。
  • 成员msg_control指向缓冲区,根据msg_flags的值,会放入不同的值。
  • 成员msg_controllen为msg_control指向缓冲区的大小。
  • 成员msg_flags为操作的方式。

1.6 使用sendmsg()函数发送数据

函数sendmsg()可用于向多个缓冲区发送数据,函数原型如下所示。函数sendmsg()向套接字描述符s中按照结构msg的设定写入数据,其中操作方式由flags指定。

复制代码
    #include <sys/uio.h>
    ssize\_t sendmsg(int s, const struct msghdr\* msg, int flags);
    
    
    
      
      
      
    

2. select()函数和pselect()函数

2.1 select()函数

select()函数是Linux中的一个系统调用,用于监视文件描述符的变化情况,包括读、写、异常等。它可以等待多个文件描述符中的任何一个变为“准备好”的状态,从而完成 I/O 操作。select()函数的语法如下:

复制代码
    #include <sys/select.h>
    int select(int nfds, fd_set \*readfds, fd_set \*writefds, fd_set \*exceptfds, struct timeval \*timeout);
    
    
    
      
      
      
    

其中,nfds 是需要监视的文件描述符数量,readfds、writefds 和 exceptfds 分别是需要监视的读、写和异常事件的文件描述符集合。timeout 是超时时间,如果在超时时间内没有任何事件发生,则 select() 函数返回 0。

select()函数返回值:
负值:select错误
正值:表示某些文件可读或可写

通常会和如下几个函数一起使用:

复制代码
    void FD\_ZERO(fd_set \*set);//清空一个文件描述符的集合
    void FD\_SET(int fd, fd_set \*set);//将一个文件描述符添加到一个指定的文件描述符集合中
    void FD\_CLR(int fd, fd_set \*set);//将一个指定的文件描述符从集合中清除;
    int  FD\_ISSET(int fd, fd_set \*set);//检查集合中指定的文件描述符是否可以读写
    
    
    
      
      
      
      
      
    

举个例子,下面的代码使用 select() 函数实现了一个简单的 TCP 服务器:

复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    int main(int argc, char \*argv[]) {
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    
    struct sockaddr\_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(8080);
    
    if (bind(listen_fd, (struct sockaddr \*)&addr, sizeof(addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    
    if (listen(listen_fd, 10) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    fd_set read_fds;
    FD\_ZERO(&read_fds);
    FD\_SET(listen_fd, &read_fds);
    
    while (1) {
        fd_set tmp_fds = read_fds;
        if (select(listen_fd + 1, &tmp_fds, NULL, NULL, NULL) == -1) {
            perror("select");
            exit(EXIT_FAILURE);
        }
    
        for (int i = 0; i <= listen_fd; i++) {
            if (FD\_ISSET(i, &tmp_fds)) {
                if (i == listen_fd) {
                    int conn_fd = accept(listen_fd, NULL, NULL);
                    if (conn_fd == -1) {
                        perror("accept");
                        exit(EXIT_FAILURE);
                    }
                    printf("accept connection %d\n", conn_fd);
                    FD\_SET(conn_fd, &read_fds);
                } else {
                    char buf[1024];
                    ssize\_t n = recv(i, buf, sizeof(buf), 0);
                    if (n <= 0) {
                        printf("close connection %d\n", i);
                        close(i);
                        FD\_CLR(i, &read_fds);
                    } else {
                        buf[n] = '\0';
                        printf("recv from connection %d: %s", i, buf);
                    }
                }
            }
        }
    }
    
    return 0;
    }
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

这个服务器使用 select() 函数监听了一个 TCP 端口,并在有新连接到来时接受连接。当有数据到来时,它会打印出数据内容。这个服务器可以同时处理多个连接,并且不会阻塞在任何一个连接上。

2.2 pselect()函数

pselect() 函数是 select() 函数的一个变种,它可以在等待期间阻塞指定的信号。pselect() 函数的语法如下:

复制代码
    #include <sys/select.h>
    int pselect(int nfds, fd_set \*readfds, fd_set \*writefds, fd_set \*exceptfds, const struct timespec \*timeout, const sigset\_t \*sigmask);
    
    
    
      
      
      
    

其中,sigmask 是一个指向信号集的指针,用于阻塞指定的信号。timeout 是超时时间,如果在超时时间内没有任何事件发生,则 pselect() 函数返回 0。

pselect() 函数与 select() 函数的区别在于,它可以在等待期间阻塞指定的信号。这个特性可以用于实现更加复杂的 I/O 操作,例如同时等待多个文件描述符和多个信号。

举个例子,下面的代码使用 pselect() 函数实现了一个简单的 TCP 服务器:

复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    int main(int argc, char \*argv[]) {
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    
    struct sockaddr\_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(8080);
    
    if (bind(listen_fd, (struct sockaddr \*)&addr, sizeof(addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    
    if (listen(listen_fd, 10) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    fd_set read_fds;
    FD\_ZERO(&read_fds);
    FD\_SET(listen_fd, &read_fds);
    
    sigset\_t sigmask;
    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
    
    while (1) {
        fd_set tmp_fds = read_fds;
        struct timespec timeout = { .tv_sec = 1 };
    
        if (pselect(listen_fd + 1, &tmp_fds, NULL, NULL, &timeout, &sigmask) == -1) {
            perror("pselect");
            exit(EXIT_FAILURE);
        }
    
        for (int i = 0; i <= listen_fd; i++) {
            if (FD\_ISSET(i, &tmp_fds)) {
                if (i == listen_fd) {
                    int conn_fd = accept(listen_fd, NULL, NULL);
                    if (conn_fd == -1) {
                        perror("accept");
                        exit(EXIT_FAILURE);
                    }
                    printf("accept connection %d\n", conn_fd);
                    FD\_SET(conn_fd, &read_fds);
                } else {
                    char buf[1024];
                    ssize\_t n = recv(i, buf, sizeof(buf), 0);
                    if (n <= 0) {
                        printf("close connection %d\n", i);
                        close(i);
                        FD\_CLR(i, &read_fds);
                    } else {
                        buf[n] = '\0';
                        printf("recv from connection %d: %s", i, buf);
                    }
                }
            }
        }
    }
    
    return 0;
    }
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

这个服务器使用 pselect() 函数监听了一个 TCP 端口,并在有新连接到来时接受连接。当有数据到来时,它会打印出数据内容。这个服务器可以同时处理多个连接,并且不会阻塞在任何一个连接上。

pselect() 函数和 select() 函数基本上是一致的,但是有三个区别:

  1. select 函数用的 timeout 参数,是一个 timeval 的结构体(包含秒和微秒),而 pselect 用的是一个 timespec 结构体(包含秒和纳秒)。
  2. select 函数可能会为了指示还剩多长时间而更新 timeout 参数,而 pselect 不会改变 timeout 参数。
  3. select 函数没有 sigmask 参数,当 pselect 的 sigmask 参数为 null 时,两者行为时一致的。
    因此,当需要阻塞指定的信号时,可以使用 pselect() 函数。

以下是一个需要阻塞信号的 pselect() 函数的例子:

复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <sys/select.h>
    #include <sys/time.h>
    #include <unistd.h>
    
    static void sig\_handler(int signo)
    {
    printf("Caught signal %d\n", signo);
    }
    
    int main(void)
    {
    fd_set rfds;
    struct timeval tv;
    int retval;
    
    /\* Watch stdin (fd 0) to see when it has input. \*/
    FD\_ZERO(&rfds);
    FD\_SET(0, &rfds);
    
    /\* Wait up to five seconds. \*/
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    
    /\* Set up the signal handler. \*/
    struct sigaction sa;
    sa.sa_handler = sig_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
    
    /\* Block SIGINT and wait for input. \*/
    sigset\_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        perror("sigprocmask");
        exit(EXIT_FAILURE);
    }
    
    retval = pselect(1, &rfds, NULL, NULL, &tv, &mask);
    
    if (retval == -1)
        perror("pselect()");
    else if (retval)
        printf("Data is available now.\n");
    else
        printf("No data within five seconds.\n");
    
    exit(EXIT_SUCCESS);
    }
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

3. poll()函数和ppoll()函数

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

“No data within five seconds.\n”);

复制代码
    exit(EXIT_SUCCESS);
    
    
      
    

}

复制代码
    ## 3. poll()函数和ppoll()函数
    
    
    
    [外链图片转存中...(img-wZG83twE-1715563799940)]
    [外链图片转存中...(img-CgxS13Hd-1715563799941)]
    
    **网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。** **[需要这份系统化的资料的朋友,可以添加戳这里获取]()** **一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

全部评论 (0)

还没有任何评论哟~