6.read()/write()
6.1 包含头文件
#include <unistd.h>
6.2 函数主体
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
参数说明:
- int fd
该参数指明从文件描述符fd的缓冲区读/写。
- void *buf
read()函数读到的数据将保存到buf所指向的空间中,该参数为void*(无类型指针)表明用户可以传入任何类型的指针,一般我们可以传入char型数组或指针来接收数据,但是如何接收int类型的数据,我目前还没有办法,欢迎大佬指点~。
- size_t count
这参数限制了读取/写入的字节数,即如果读缓冲区有20字节数据,如果调用read()时count设置为10,则只会返回10个字节数据。如果读缓冲区有7字节数据,如果调用read()时count设置为10,则read()函数会正常返回7字节的数据,而不会出现错误。
- const void *buf
这里加const表明指向常量的指针。
6.3 返回值
成功将返回读取/写入的字节数,返回的数小于请求的字节数,也属于成功返回;size_t 和 ssize_t 分别表示无符号整型数据和有符号整型数据。
错误返回-1,错误类型如下:
错误类型 | 解释 |
---|---|
EAGAIN | 文件描述符 fd 引用套接字以外的文件,并已标记为非阻塞 (O_NONBLOCK),读取/写入将阻塞。 |
EBADF | fd 不是有效的文件描述符或未打开。 |
EFAULT | buf 位于可访问的地址空间之外。 |
EINTR | 在读取数据的第一个字节之前,呼叫被信号中断; |
EISDIR | fd指向一个目录 |
6.4 结束
read()和write()用法较为简单,但是针对这两个函数进一步补充说明:
- read()和write()函数并不是专门为socket编程设计的,socket编程读写操作推荐用send()/recv()函数。而read()和write()适用于所有的文件读写,因为Linux下一切皆文件,所以每个套接字都可以看作一个文件,也可以使用read()和write()函数进行读写操作。
- socket是全双工的,这就表明通信的双方建立socket连接后,是可以同时进行读写操作的,因为socket的读缓冲区和写缓冲区是独立的两个通道,互补影响。
- 读写操作时,还需掌握一个概念:文件偏移量,该偏移量指明了目前打开的文件描述符下一次读取/写入数据时的位置。事实上,内核使用3种数据结构来表示打开的文件,这点在之后的文件IO文章种将详细介绍,此处重点谈论socket,故不再赘述。
- 简单示例代码
#include<iostream>
#include<unistd.h>
#include<string.h> //memset()函数需要头文件
#include<sys/types.h> //open()函数包含头文件
#include<sys/stat.h> //open()函数包含头文件
#include<fcntl.h>
#define handle_error(msg) do {perror(msg); exit(EXIT_FAILURE); } while (0)
using namespace std;
int main()
{
const char *ch = "hello! \n";
// int buf[80];
char *buff = new char[80];
// memset(buf,0,sizeof(buf));
int fd = -1;
int ret = -1;
fd = open("/home/root/test/test.txt",O_RDWR|O_APPEND); //以可读/可写、追加的方式打开文件
ret = read(fd,buff,5); //从fd中读取20个字符
cout<<buff<<endl;
ret = write(fd,ch,strlen(ch)); //在文档末尾写入字符
if (ret == -1){
handle_error("write");
}
delete [] buff; //释放内存
close(fd); //关闭文件
return 0;
}
这个项目算是一个进阶,可以了解一下:代码传送门