Linux IO函数

本文介绍了Linux系统中用于网络编程的IO函数,包括read()、write()、recv()、send()、readv()、writev()、recvmsg()和sendmsg()。这些函数在处理套接字数据时各有特点,如read()和write()的基本读写操作,recvmsg()和sendmsg()的通用性。同时,文章提到了与标准IO函数的区别,特别是使用标准IO函数库在套接字上的潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux网络编程中,针对套接字读写的函数有很多,下面介绍一些函数的常见用法;这里介绍的函数都是linux系统中的特有函数,与C标准库中的IO函数存在一定的区别。

1、read() 和 write()函数
  
read() 和 write() 函数是Linux中最基本的读写函数,可以用于各种数据的读写。

/* 返回值:成功则为读取或写入的字节个数,失败为-1; 
 * 定义:
 */
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbyte); 
ssize_t write(int fd, const void* buf, size_t nbytes);
/* 说明
 * fd 为描述符,可以是文件描述符,也可以是套接字描述符;
 * buf 用于存储读取或写入的数据;
 * nbyte 记录字节数;
 * 根据不同的应用情况,返回值可能小于请求的字节数
 */

一般来说,对于常规文件的读写不会阻塞,函数一定会在有限的时间内返回,但是从终端设备或网络读取时就不一定了,如果网络通信消息一直没有到达,那么读取函数就一直阻塞等待,终端还没有进行输入时,也是处于阻塞状态。返回的字节数包含换行符。
阻塞和非阻塞read/write

2、recv() 和 send() 函数

这两个函数类似于read() 和 write() 函数,但是这两个函数只用于套接字相关的数据处理。

/* 返回值:成功则为读取或写入的字节个数,失败为-1;
 * 定义:
 */ 
#include <sys/socket.h>
ssize_t recv(int fd, void *buf, size_t n, int flags);
ssize_t send(int fd, const void *buf, size_t n, int flags);
/* 前面3个参数与read/write中参数一样,flags的值可为0或下面的值:
 * 1、MSG_DONTROUTE:绕过路由表查找,从直接连接的本机网络上处理数据;
 * 2、MSG_DONTWAIT:仅对本操作执行非阻塞;
 * 3、MSG_OOB:即将发送或接受带外数据;
 * 4、MSG_PEEK:查看已可读取的数据,且系统在recv或recvfrom返回后不丢弃这些数据;
 * 5、MSG_WAITALL:尚未读取请求数目的字节之前读取操作不要返回;
 */

3、readv() 和 writev() 函数

read() 和 write() 函数在处理数据时数据是连块的,即处理的数据是保存在一个连续的地址空间中的,但是有时候需要一次处理多个在不同地址空间的数据,readv函数可以将数据的输入分散到多个缓冲区中,称为分散读;writev函数可以将多个缓冲区的输出数据写入一个连续的缓冲区内,称为集中写;

/* 返回值:成功则为读入或写出的字节数,出错则为-1;
 * 定义:
 */
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec{
    void *iov_base;
    size_t iov_len;
}
/* 说明:
 * 参数fd为文件或套接字描述符,iovcnt为结构指针iov中元素个数,最大一般不超过1024;
 * 结构iovec表示一个数据元素,iov_base为数据起始地址,iov_len为数据区大小;
 * 数组指针iov中有几个元素就有几块分散的缓冲区地址块;
 * 两个函数可以用于任何描述符。不仅限于套接字;
 */

参考:readv()和writev()函数

4、recvmsg() 和 sendmsg() 函数

这两个函数只用于套件字的数据处理,是最通用的两个函数,包含前面的几个函数具有的各项功能。

/* 返回值:成功则为读入或写出的字节数,出错则为-1;
 * 定义:
 */
#include <sys/socket.h>
ssize_t  recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t  sendmsg(int sockfd, struct msghdr *msg, int flags);

struct msghdr{
    void        *msg_name;        /*protocol address*/
    socklen_t   msg_namelen;      /*size of protocol address*/
    struct iovec  *msg_iov;       /*scatter/gather array*/
    int         msg_iovlen;       /*#elements in msg_iov*/
    void        *msg_control;     /*ancillary data (cmsghdr struct)*/
    socklen_t   msg_controllen;   /*length of ancillary data */
    int         msg_flags;        /*flags returned by recvmsg()*/
}
/* 说明:
 * sockfd为套接字描述符,msg为结构参数指针,flags为标志;
 * msghdr中msg_name,msg_namelen为未连接的套接字地址及其长度;
 * 对于已连接的套接字msg_name直接设为NULL;
 * msgiov,msg_iovlen与readv函数第2,3个参数类似;
 * msg_control,msg_controllen为辅助数据,与结构cmsghdr相关;
 * msg_flags为标志,只有函数recvmsg使用;
 * flags用于输入一定的控制信息,而msg_falgs一般用于返回标志信息;
 * 函数send,recv;sendto,recvfrom,sengmsg,recvmsg都存在flags标志,部分标志在
 * recv函数介绍时已列出,其它参考UNIX网络编程卷1;
 */

5组IO函数比较,yes表示符合说明,空格表示不符合。

函数任何描述符仅套接字描述符单个读写缓冲区分散/集中读写可选标志可选对端地址可选控制信息
read,writeyesyes
readv,writevyesyes
send,recvyesyesyes
sendto,recvfromyesyesyesyes
sendmsg,recvmsgyesyesyesyesyes

上面介绍的几组IO函数都是围绕描述符工作的,通常是作为UNIX内核中的系统调用实现;执行IO的另一种方式是使用标准IO函数库,由ANSI C标准规范,便于移植到支持C的非Unix系统上。

  标准IO函数也可以用于套接字上:通过调用fdopen从任何描述符创建一个标准IO流;相反可以通过fileno获取任何一个给定标准IO流的描述符。
  标准IO函数执行以下3类缓冲:
  1、完全缓冲,只在下列情况下才发生IO(数据输出):缓冲区满,进程显式调用fflush,进程调用exit终止自身;标准缓冲区大小一般为8192字节。
  2、行缓冲,只在下列情况下才发生IO:碰到一个换行符,进程调用fflush,进程调用exit终止自身。
  3、不缓冲,每次调用标准IO输出函数都发生IO。
  标准IO函数大多数Unix实现遵循以下规则:
  - 标准错误输出总是不缓冲;
  - 标准输入标准输出完全缓冲,除非他们指代终端设备(这种情况下行缓冲);
  - 所有其他IO流都是完全缓冲,除非他们指代终端设备(这种情况下行缓冲);

因为套接字并不是终端设备,因此在用标准IO处理套接字数据IO时,需要考虑缓冲问题,然而这样可能会引起一些潜在问题,所以一般情况下尽量避免在套接字上使用标准IO函数库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值