read/write
readv/writev
“对数据进行整合传轮及发送的函数。”
也就是说,通过writev 函数可以将分散保存在多个缓冲中的数据一并发送,通过readv函数可以由多个缓冲分别接收。因此,适当使用这2个函数可以减少I/O函数的调用次数。下面先介绍writev 函数。
#include <sys/uio.h>
ssize t writev(int filedes, const struct iovec * iov, int iovcnt);
//成功时返回发送的字节数,失败时返回-1 。
#filedes 表示数据传输对象的套接字文件描述符。但该函数并不只限于套接字,因此,可以像read函数一样向其传递文件或标准输出描述符。
#iov iovec结构体数组的地址值,结构体 iovec 中包含待发送数据的位置和大小信息。
#iovcnt 向第二个参数传递的数组长度。
合理使用readv & writev 函数
哪种情况适合使用readv和writev函数? 实际上,能使用该函数的所有情况都适用。例如, 需要传输的数据分别位于不同缓冲(数组)时,需要多次调用write函数。此时可以通过1 次writev函数调用替代操作,当然会提高效率。同样,需要将输入缓冲中的数据读入不同位置时,可以不必多次调用read函数,而是利用1 次readv函数就能大大提高效率。
即使仅从C语言角度看,减少函数调用次数也能相应提高性能。但其更大的意义在于减少数据包个数。假设为了提高效率而在服务器端明确阻止了Nagle算法。其实writev函数在不采用Nagle算法时更有价值,如图所示。
上述示例中待发送的数据分别存在3个不同的地方,此时如果使用write 函数则需要3次函数调用。但若为提高速度而关闭了Nagl哆垃~. 则极有可能通过3个数据包传递数据。反之,若使用writev函数将所有数据一次性写入输出缓冲,则很有可能仅通过1个数据包传输数据。所以wri tev 函数和readv函数非常有用。
再考虑一种情况: 将不同位置的数据按照发送顺序移动(复制)到l个大数组,并通过1 次write函数调用进行传输。这种方式是否与调用writev函数的效果相同?当然! 但使用writev函数更为便利。因此,如果遇到writev 函数和readv函数的适用情况,希望各位不要错过机会。
参考:https://blog.csdn.net/qq_40732350/article/details/88980011
recv/send
#include <sys/socket.h>
ssize_t recv(int sockfd, void* buf, size_t nbytes, int flags);
//成功时返回接收的字节数(收到EDF 时返回0)' 失败时返回-1 。
#sockfd 表示数据接收对象的连接的套接字文件描述符。
#buf 保存接收数据的缓冲地址值。
#nbytes 可接收的最大字节数。
#flags 接收数据时指定的可选项信息。
#include <sys/socket.h>
ssize_t send(int sockfd, const void * buf, size_t nbytes, int flags);
//成功时返回发送的字节数,失败时返回-1。
#sockfd 表示与数据传输对象的连接的套接字文件描述符。
#buf 保存待传输数据的缓冲地址值。
#nbytes 待传输的字节数。
#flags 传输数据时指定的可选项信息。
由于不能绑定IP地址,所以只能借助于connect来实现连接和发送和接收信息。
参考:https://blog.csdn.net/qq_40732350/article/details/88980011
recvmsg/sendmsg
#include <sys/socket.h>
ssize_t recvmsg(int socket, struct msghdr *message, int flags);
ssize_t sendmsg (int __fd, const struct msghdr *__message, int __flags);
struct msghdr
{
void *msg_name; /*源地址 */
socklen_t msg_namelen; /* 地址长度 */
struct iovec *msg_iov; /*接收或发送数据的数组 */
size_t msg_iovlen; /*msg_iov数组的元素数量*/
void *msg_control; /*指向缓冲区*/
size_t msg_controllen; /*指向缓冲区的大小*/
int msg_flags; /*操作方式*/
};
参考:https://wenku.baidu.com/view/7a8a522158fb770bf78a55cf.html
sendto/recvfrom
基于UDP 的数据I/O函数
#include <sys/socket.h>
ssize_t sendto(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);
//成功时返回传输的字节数,失败时返回-1 。
#sock 用于传输数据的UDP套接字文件描述符。
#buff 保存待传输数据的缓冲地址值。
#nbytes 待传输的数据长度,以字节为单位。
#flags 可选项参数,若没有则传递0。
#to 存有目标地址信息的sockaddr结构体变量的地址值。
#addrlen 传递给参数to的地址值结构体变量长度。
#include <sys/socket.h>
ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags, struct sockaddr * from, socklen_t *addrlen);
成功时返回接收的字节数,失败时返回-1。
#sock 用于接收数据的UDP套接字文件描述符。
#buff 保存接收数据的缓冲地址值。
#nbytes 可接收的最大字节数,故无法超过参数butt所指的缓冲大小。
#flags 可选项参数,若没有则传入0 。
#from 存有发送端地址信息的sockadd结构体体变量的地址值。
#addrlen 保存参数from的结构体变量长度的变量地址值。
参考:https://blog.csdn.net/qq_40732350/article/details/88945957
函数read()/write()和readv()/writev()可以对所有的文件描述符使用:recv()/send), recvfrom()/writeto0)和recvmsg/sendmsg只能操作套接字描述符。
函数readv()/writev()和recvmsg()/sendmsg)可以操作多个缓冲区, read()/write()、recv()/send()和recvfrom()/sendto()只能操作单个缓冲区。
函数recv()/send()、recvfrom()/sendto()和recvmsg()/sendmsg()具有可选标志。
函数recvfrom()/sendto()和recvmsg()/sendmsg()可以选择对方的IP地址。
函数recvmsg()/sendmsg()有可选择的控制信心,能进行高级操作。
IO函数比较