Linux网络编程API(二)

2022-09-26 17:39:28 浏览数 (1)

相关API笔记(二)

Linux网络编程高级I/O函数

1. pipe

pipe函数用于创建一个管道,实现进程间通信

代码语言:javascript复制
#include <unistd.h>
//成功返回0,失败返回-1并设置errno
int pipe(int fd[2]);

参数:

fd: pipe函数创建的两个文件描述符对应管道两端,分别为“读”端“写端”(可记忆为读写),即fd[0]为管道的读端,fd[1]为管道的写端。

默认情况下这对文件描述符是阻塞的,对空的fd[0]执行读操作对满的fd[1]执行写操作会阻塞。

创建双向管道

代码语言:javascript复制
#include <sys/types.h>
#include <sys/socket.h>
//成功返回0,失败返回-1并设置errno
int socketpair(int domain, int type, int protocol, int fd[2]);

参数:

前三个参数与socket系统调用的三个参数完全相同,但是domian只能使用UNIX本地域协议族AF_UNIX,因为仅能在本地使用这个双向管道

fd: 与pipe系统调用的参数一样,不过这里创建的文件描述符都是即可读又可写的

2. dup和dup2

代码语言:javascript复制
#include <unistd.h>
//二者都是失败返回-1并设置errno
int dup(int file_descriptor);
//dup2可理解为后者重定向到前者
int dup2(int file_descriptor_one, int file_descriptor_two);

参数:

dup和dup2的参数都是文件描述符,具体作用如下:

dup函数创建一个新的文件描述符,该新的描述符和原有文件描述符file_descriptor指向相同文件,管道或者网络连接, 并且dup返回的文件描述符总是取系统当前可用的最小整数值

dup2函数与dup类似,不过它返回的是第一个不小于file_descriptor_tow的整数值

代码语言:javascript复制
//例子。重定向STDOUT
//使用dup
void dup_out(){
    //...
    close(STDOUT);
    dup(fd);
    printf("dup");
    close(fd);
}

//使用dup2
void dup2_out(){
    dup2(fd, STDOUT);
    printf("dup2");
    close(fd);
}

3. readv和writev

readv函数将数据从文件描述符读到分散的内存块中,即分散读

writev函数则将多块分散的内存数据一并写入文件描述符中,即集中写

代码语言:javascript复制
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec* vector, int count);
ssize_t writev(int fd, const struct iovec* vector, int count);
//iovec封装了一块内存的起始位置和长度
struct iovec{
    void*		iov_base;				//内存起始地址
    size_t 		iov_len;				//这块内存的长度
};

参数:

fd: 被操作的目标文件描述符

vector: iovec结构数组

count: vector数组的长度

代码语言:javascript复制
//eg
char buf1[1024], buf2[1024];
//...往buf1填充数据
struct iovec iv[2];
iv[0].iov_base = buf1;
iv[0].iov_len = sizeof(buf1);
iv[1].iov_base = buf2;
iv[1].iov_len = sizeof(buf2);
//writev,集中写,把buf1,buf2中的数据写到connfd
ret = writev(connfd, iv, 2);
//readv,分散读,把键盘的输入读出并放入到buf1,buf2中
ret = readv(STDIN_FILENO, iv, 2);

3. sendfile

sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),避免在内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,称为零拷贝

代码语言:javascript复制
#include <sys/sendfile.h>
//成功返回传输的字节数,失败返回-1并设置errno
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);

参数:

out_fd: 待写入内容的文件描述符,可理解为数据进入到out_fd输出,流到in_fd,我们需要把数据写给它让它又得输出。

in_fd: 待读出内容的文件描述符,可理解为文件流入到in_fd中,我们需要从它这里读出数据。

offset: 指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。

count: 指定在文件描述符in_fd和out_fd之间传输的字节数

4. mmap和munmap

mmap用于申请一段内存空间,这段内存可以作为进程间通信的共享内存,可以将文件直接映射到其中。

munmap函数则释放mmap创建的这段内存空间

代码语言:javascript复制
#include <sys/mman.h>
//成功返回指向目标区域的指针,失败返回MAP_FAILED((void*)-1)
void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
//成功返回0,失败返回-1并设置errno
int munmap(void *start, size_t length);

参数:

start: 用户指定某个特定的地址作为这段内存的起始地址,如果设置为NULL,则系统自动分配一个地址。

length: 指定内存段的长度

prot: 设置内存段的访问权限

含义

PROT_READ

内存段可读

PROT_WRITE

内存段可写

PROT_EXEC

内存段可执行

PROT_NONE

内存段不能访问

flags:可控制内存段内容被修改后程序的行为

5. splice

splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作

代码语言:javascript复制
#include <fcntl.h>
//成功返回移动字节的数量,失败返回-1并设置errno
ssize_t splice(int fd_in, loof_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags);

参数:

fd_in: 待输入数据的文件描述符

off_in: 如果fd_in是管道文件描述符,则必须设置为NULL,若不是(如socket),则off_in表示从输入数据流的何处开始读取数据。

fd_out: 待输出数据的文件描述符

off_out: 同off_in

len: 指定移动数据的长度

flags: 可被设置为如下某些值的按位或

使用splice时,fd_in和fd_out至少有一个是管道文件描述符

其常见errno:

6. tee

tee函数用于在两个管道文件描述符之间复制数据,也是零拷贝操作

代码语言:javascript复制
#include <fcntl.h>
//成功返回复制的数据量,失败返回-1并设置errno
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

参数:

与splice相同,但是fd_infd_out必须都是管道文件描述符

代码语言:javascript复制
//eg
//把管道pipe1的输出端数据复制到管道pipe2的输入端
int ret = tee(pipe1[0], pipe2[1], 32768, SPLICE_F_NONBLOCK);

7. fcntl

fctnl(file control), 提供对文件描述符的各种控制操作

代码语言:javascript复制
#include <fctnl.h>
//成功返回值见后面表格,失败返回-1并设置errno
int fcntl(int fd, int cmd, ...);

参数:

fd: 被操作的文件描述符

cmd: 操作类型,根据操作类型可能还需要第三个可选参数arg

对于F_SETFL,可更改的几个标志如下面的描述:

  • O_NONBLOCK 非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
  • O_APPEND 强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
  • O_DIRECT 最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
  • O_ASYNC 当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候
代码语言:javascript复制
//eg
int setnonblocking(int fd){
    int old_option = fctnl(fd, F_GETFL);	/* 获取文件描述符旧的状态标志 */
    int new_option = old_option | O_NONBLOCK;	/* 设置非阻塞标志 */
    fcntl(fd, F_SETFL, new_option);
    return old_option;	/* 返回文件描述符旧的状态标志,以便日后恢复该状态标志 */
}

0 人点赞