欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux 下读写函数的介绍(系统调用、c库函数、socket通信)

程序员文章站 2024-01-21 21:05:10
...

一、系统调用读写函数
1、write函数
原型:ssize_t write (int fd, const void * buf, size_t count);
说明:write()会把参数buf 所指的内存写入count 个字节到参数fd 所指的文件内.
返回值:
(1)如果顺利write()会返回实际写入的字节数.
(2)当有错误发生时则返回-1, 错误代码存入errno 中.
错误代码:
EINTR 此调用被信号所中断.
EAGAIN 当使用不可阻断I/O 时 (O_NONBLOCK), 若无数据可读取则返回此值.
EADF 参数fd 非有效的文件描述词, 或该文件已关闭.

2、read函数
原型:ssize_t read(int fd, void * buf, size_t count);
说明:read()会将文件fd 上的数据读取count字节存储到参数buf
返回值:
(1)如果成功,返回读取的字节数;
(2)如果出错,返回-1并设置errno;
(3)如果在调read函数之前已是文件末尾,则返回0
如果顺利 read()会返回实际读到的字节数, 最好能将返回值与参数count 作比较,
若返回的字节数比要求读取的字节数少, 则有可能读到了文件尾

二、c标准库读写库函数
1、fread函数
原型;size_t fread(void *ptr,size_t size,size_t nitems,FILE *stream);
说明:阻塞等待获取指定数量的数据
2、fwrite函数
原型;siize_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream);、
说明:阻塞等待输入的数据量达到指定量后输出
3、fgetc函数
原型:int fgetc(FILE *stream);
说明:获取一个字符
4、fputc函数
原型:int fputc(int c, FILE *stream);
说明:往指定流写入一个字符
5、getchar函数
原型:int getchar(void);
说明;getchar 函数的作用就相当于 getc(stdin),它直接从标准输入里读取下一个字符。
6、putchar函数
原型:int putchar(int c);
说明:往标准输出流中写入数据
7、getc函数
原型:int getc(FILE *stream);
说明:getc 函数的作用和 fgetc 函数一样,但是,getc 函数可以被实现为宏
8、putc函数
原型:int putc(int c, FILE *stream);
9、fgets函数
原型:char *fgets(char *s, int size, FILE *stream);
说明:
fgets 函数把读到的字符写到 s 指向的字符串里,直到:
1)遇到换行符,则停止读入字符,并将遇到的换行符一起传递给接收字符串,再加上一个表示结尾的空字节 \0;
2)已经传输了 n-1 个字符,则加上一个空字节 \0 结尾后,停止读入字符;
3)到达文件尾(EOF)。
10、fputs函数
原型;int fputs(const char *s, FILE *stream);
11、puts函数
原型:int puts(const char *s);

示例:

int count;
char buf[1024];
char c;

printf("test fgetc and fputc\n");
c = fgetc(stdin);
fputc(c,stdout);
fputc('\n', stdout);

printf("test fgets and fputs\n");
fgets(buf, 1024, stdin);
fputs(buf, stdout);
fputs("\n", stdout);

printf("test fread and fwrite\n");
fread(buf, 1, 10, stdin);
fwrite(buf, 1, 10, stdout);

三、socket套接字读写函数
#include <sys/types.h>
#include <sys/socket.h>

UDP 读写

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
				struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:之前创建的socket文件描述符,从这个文件描述符中读取数据
buf:用来存放接收数据的缓冲区
len:该缓冲区的大小,这里注意有坑,需要用sizeof(buf)-1,因为这里还需要一个空间的大小的来存放'\0'。
flags:一般设置为0
src_addr:因为UDP没有通信连接的概念,因此每次接收数据都需要获取到发送端的socket地址(下面的sendto函数也是类似),src_addr就是发送端的源地址,对于服务器来说,这也就是客户端来发送的地址。从sockfd中读取到发送端的socket地址放到这个src_addr结构体中
addrlen:源地址结构体的大小,注意这里的类型是socklen_t*,需要&sizeof(src_addr),这也是一个输入输出型参数
返回值:失败返回-1,成功返回实际接收数据的大小。		
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 
				const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd:之前创建的socket文件描述符
buf:要发送的数据在buf当中
len:这里和recvfrom不同的是,此处不需要sizeof(buf)-1,因为这里的buf数组可能不是满的,它的中间可能有'\0'sizeof(buf)得到的是整个缓冲区的大小,strlen(buf)得到的大小是到第一个'\0'的大小,所以此处需要用的是strlen(buf)
flags:一般设置为0
dest_addr:指要把数据发送给哪个目的IP,如果是客户端给服务器发消息,那么dest_addr就是服务器的socket地址
addrlen:目的IP的大小,注意这里又有和recvfrom不同的地方,前面的addrlen的类型的是指针,这里不是指针,所以直接用sizeof(dest_addr)即可,因为这不是一个输入输出型参数
返回值:失败返回-1,成功返回传送的数据的大小

注:
recvfrom函数和sendto函数还可以用于面向连接的socket读写,只需要把最后两个参数都设置为NULL就可以了,因为我们已经知道对端的socket地址了,这就有点像下面要将的recv和send了。
TCP 读写

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd是之前创建的文件描述符,并且是已经命名过(bind)和设置监听了的(listen)
buf:用来存放接收数据的缓冲区
len:该缓冲区的大小,注意和recvfrom中的一样也要留一个空间给'\0'
flags:一般置为0

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd是之前创建的文件描述符,并且是已经命名过(bind)和设置监听了的(listen)
buf:发送的是buf缓冲区中的数据
len:缓冲区实际的有效大小(字符串就用strlen)
flags:一般置为0

通用数据读写

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
socket编程接口还提供了一对通用的数据读写接口。它们不仅能用于TCP,也能用于UDP
sockfd参数指定被操作的目标socket
msg参数是msghdr结构体指针,该结构体定义如下:
struct msghdr {
    void*         msg_name;       /* socket地址 */
    socklen_t     msg_namelen;    /* socket地址长度 */
    struct iovec* msg_iov;        /* 分散的内存块 */
    size_t        msg_iovlen;     /* 分散的内存块数量 */
    void*         msg_control;    /* 指向辅助数据的起始位置 */
    size_t        msg_controllen; /* 辅助数据的大小 */
    int           msg_flags;      /* 复制函数中的flags参数,并在调用过程中更新 */
};

struct iovec {                    
    void  *iov_base;              /* 内存起始地址 */
    size_t iov_len;               /* 这块内存的长度 */
};
msg_name成员指向一个socket地址结构变量。它指定通信对方的socket地址。对于面向连接的TCP协议,该成员没有意义,必须被置为NULL
msg_namelen成员指定了msg_name成员所指的socket地址的长度
msg_iov成员指向一个iovec结构体,该结构体封装了一块内存的起始位置和长度
msg_iovlen指定这样的结构体有几个。对于recvmsg而言,数据将被读取并存放在msg_iovlen块分散的内存中,这些内存的位置和长度由msg_iov指向的数组指定,这称为分散读;对于sengmsg而言,msg_ioven块分散内存中的数据将被一并发送,这称为集中写。
msg_control和msg_controllen用于辅助数据的传送
msg_flags成员无需设定,它会复制传入的flags参数。recvmsg还会在调用结束前,将某些更新后的标志设置到msg_flags中。

与文件读写一致的读写函数
#include <unistd.h>
ssize_t read (int fd, void *buf, size_t count);
ssize_t write (int fd, const void *buf, size_t count);