在Windows文件指的就是普通的肉眼可见的文件 , 而Linux一切皆文件 https://blog.csdn.net/nan_nan_nan_nan_nan/article/details/81233599
一定要注意生成文件的警告和报错,不能忽略了!!!!!!!
#include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { FILE* fp; char buf[]="hello world"; char buff2[128]; if((fp = fopen("1.txt","w+")) == NULL) { perror("file open failure"); exit(1); } fwrite(buf , sizeof(buf), 1 , fp); memset(buff2,0,sizeof(buff2)); fseek(fp,0,SEEK_SET); fread(buff2 ,sizeof(buff2) ,1, fp); printf(">>%s ",buff2); getchar(); fclose(fp); return 0; }
1.标准流和流功能 write和read 可以对任何文件读写
stdin 0 标准输入
stdout 1 标准输出
stderr 2 标准错误(报错)
可以使用write代替printf , printf是实现比较复杂局限性也小
write(目的/0, buff , length) 目的是写入的目标文件或使用上面的std进行打印和输出到控制台 , buff是要写入的数据 , length是写入的大小
FILE* 是指向一个内存 open返回的是一个句柄
2.缓冲区
1.缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
2.缓冲区有分为下列几种
_IOFBF 全缓冲 | 全缓冲区 默认大小BUFSIZE 4096 fflush() https://baike.baidu.com/item/fflush/10942194?fr=aladdin 默认开启全缓冲区
_ IOLBF 行缓冲 | 行缓冲区 遇到换行符才进行刷新 参考Linux终端和scanf
_IONBF 无缓冲 | 无缓冲区 stdio库 参考read, write ,stderr 都是不带缓冲区的
指定缓冲区 setbuf(FILE* stream , char* buf); buf的长度必须是指向长度为BUFSIZE的缓冲区
setbuffer(FILE* stream , char* buf , size_t size);
setlinebuf(FILE* stream);
3.设置缓冲区的函数
void setbuf(FILE *stream, char *buf);
void setbuffer(FILE *stream, char *buf, size_t size);
void setlinebuf(FILE *stream);
int setvbuf(FILE *stream, char *buf, int mode , size_t size);
4.为什么使用setvbuf函数
如果你的内存足够大,可以把文件IO的BUF设置大一些,这样每次你用fopen/fread/fwrite/fscanf/fprintf语句的时候,都会在内存里操作,减少内存到磁盘IO读写的操作次数,提高系统效率。如果你的程序的功能涉及到类似数据库、视频、音频、图像处理等大量需要爆发式磁盘到内存的IO情况下,可以考虑用setvbuf进行优化内存IO,其他情况下可以不考虑,LINUX/WINDOWS会自动处理这些问题。
3.fopen的权限 https://blog.csdn.net/gettogetto/article/details/72867757
函数原型:FILE * fopen(const char * path,const char * mode);
mode:
“r” 以只读方式打开文件,该文件必须存在。
“r+” 以可读写方式打开文件,该文件必须存在。
”rb+“ 读写打开一个二进制文件,允许读写数据(可以任意修改),文件必须存在。
“w” 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
“w+” 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
“a” 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
”a+“ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
“wb” 只写打开或新建一个二进制文件;只允许写数据(若文件存在则文件长度清为零,即该文件内容会消失)。
“wb+” 读写打开或建立一个二进制文件,允许读和写(若文件存在则文件长度清为零,即该文件内容会消失)
“ab” 追加打开一个二进制文件,并在文件末尾写数据
“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据
4.文件读写函数
write read (fd , buff , size(buff)); 任何文件都可读写 如:txt ,套接字等等...
fwrite fread (buff , sizeof(buff), 1 , fp); 只能读写标准文件
fgetc和getc他们的区别并不是在他们的使用上,而是在他们的实现上!具体来说,就是带f的(fgetc、fputc)实现的时候是通过函数来实现的,而不带f(putc、getc)的实现的时候是通过宏定义来实现的!
char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止。
int getc(FILE *stream) int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。
int puts(const char *str) 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。
int fputc(int char, FILE *stream) int putc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
5.文件流定位
iint fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
whence : (SEEK_SET 文件开始的位置)(SEEK_CUR 文件的当前位置)(SEEK_END 文件结束的的位置)
long int ftell(FILE *stream) 返回文件读写指针当前的位置距离开头的字节数
void rewind(FILE *stream) 设置文件位置为给定流 stream 的文件的开头。
需求: 获得当前文件的真实大小 实现:fseek到文件end 再 ftell 获得
6.标准文件和socket文件的区别和联系 以上所述都是标准的IO , 以下的是socketIO
区别:
文件的量 不同 (网络文件的量更加庞大)
处理的实时性要求不同 (文件不能提前获取(如电影缓冲好的和未缓冲好的区别) , 标准文件是以阻塞的方式进行的在网络文件行不通)
联系: 都是文件可以使用相同的函数
7.socket的IO模型 IO模型 https://www.cnblogs.com/LittleHann/p/3897910.html
阻塞非阻塞区别 https://baijiahao.baidu.com/s?id=1623908582750588151&wfr=spider&for=pc
以下对照IO模型
阻塞IO处理 如: read() 如果没有收到对端数据 , 内核会处于挂起状态直到有数据才会往下执行
非阻塞IO处理 如: 把read()设置成非阻塞 无论是否收到数据都会立刻返回,再不断的访问socket(也是一种阻塞) ,直到有数据才往下执行
IO复用式 一个IO复用到多个IO , 并一起等待 , 一个文件夹里有多个文件,一起阻塞到内核里
信号驱动式IO 不等待socket是否有数据, 当socket有数据时会发起信号并优先处理数据
信号驱动式IO: 当在执行程序时, 绑定的信号IO发起信号时, 挂起当前执行的程序去处理发起信号的IO,处理完信号后再继续之前的操作
异步IO: 前面所述都会占用IO,一直等待内核拷贝完数据返回后才执行下个IO 无法同时并发处理多个IO
同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读
写操作。异步文件IO也就是重叠IO允许一个或多个线程同时发出IO请求。
异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了
8.IO复用的使用与流程
IO复用: IO复用是把多个IO的fd加入到fd_set这个文件集里,多个IO共同使用一个select的fd (一起阻塞), select()轮询fd_set , 但凡有数据的IO则立刻返回
select poll epoll
selcet函数是一个轮循函数,即当循环询问文件节点,可设置超时时间,超时时间到了就立刻返回往下执行
select()的详解 https://blog.csdn.net/jiaqi_327/article/details/25657601
分配 struct fd_set rfds; 创建IO复用的文件集
设置 初始化FD_ZERO(&rfds); 加入句柄FD_SET(fd , &rfds);把fd加入到rfds 移除句柄FD_CLR(fd, &rfds));把fd移出rfds
使用 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
int nfds 文件集合中文件描述符最大值(fd是int类型,每个进程默认有3个标准fd,从3开始+1)+1
比如stdin(0) , stdout(1) , 因为select的起始位置不同,fd是从0开始的 ,select是从1开始所以要+1(0+1才能指向stdin代表第一个fd)
freadfds, writefds, exceptfds 是可读文件节点集, 可写文件节点集, 检查节点错误集,(给freadfds参数就行了其他NULL)
struct timeval *timeout 设置select的间隔时间,超过时间立刻返回
struct timeval { 创建对象设置好后传入
long tv_sec; /* seconds */ 秒
long tv_usec; /* microseconds */ 微秒};
int FD_ISSET(int fd, fd_set *set)是一个宏,不是函数,作用就是检察一下现在是否有数据可读。 通过select返回后(证明有数据)不再进行阻塞
经过第一次select后 , 每次重新select之前都要重新加入FD_SET ,因为select会改变rfds里的值
inet_aton(const char* cp,struct in_addr* inp) 将cp所指的网络地址字符串转换成网络使用的二进制的数,然后存于inp所指的in_addr结构中
inet_ntoa相反
continue回到while(1)
select的第二个参数在select之前是作为参数传入 , select之后 改变了rfds的值后作为返回数据的指针返回出来
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/types.h> #define MAXBUF 1024 int main(int argc, char **argv) { int sockfd, new_fd; socklen_t len; struct sockaddr_in my_addr, their_addr; unsigned int myport, lisnum; char buf[MAXBUF + 1]; fd_set rfds; //创建IO复用的容器 struct timeval tv; //select的超时时间 int retval, maxfd = -1; if (argv[2]) myport = atoi(argv[2]); //输入端口 else myport = 7838; if (argv[3]) lisnum = atoi(argv[3]); //输入最大连接数,也可以NULL else lisnum = 2; if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { //创建socket对象 perror("socket"); exit(EXIT_FAILURE); } bzero(&my_addr, sizeof(my_addr)); //置0 my_addr.sin_family = PF_INET; //设置本地信息 my_addr.sin_port = htons(myport); if (argv[1]) my_addr.sin_addr.s_addr = inet_addr(argv[1]); //输入本地地址 else my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { //绑定 perror("bind"); exit(EXIT_FAILURE); } if (listen(sockfd, lisnum) == -1) { //监听 perror("listen"); exit(EXIT_FAILURE); } while (1) { printf (" ----wait for new connect "); len = sizeof(struct sockaddr); if ((new_fd =accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -1) { //接受连接并保存对端信息 perror("accept"); exit(errno); } else printf("server: got connection from %s, port %d, socket %d ", inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd); while (1) { FD_ZERO(&rfds); //把IO复用的容器(rfds)置0 FD_SET(0, &rfds); //把stdin加入到 rfds FD_SET(new_fd, &rfds); //把new_fd加入到 rfds maxfd = new_fd; tv.tv_sec = 1; //超时1秒 tv.tv_usec = 0; retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); //阻塞 , 轮询加入到rfds里的fd ,超时后跳过阻塞往下执行 if (retval == -1) { perror("select"); exit(EXIT_FAILURE); } else if (retval == 0) { //如果select返回0证明没有数据更新 continue; //跳过当前循环,强制开始下一次循环 } else { if (FD_ISSET(0, &rfds)) //FD_ISSET 判断stdin是否有可读数据 { bzero(buf, MAXBUF + 1); fgets(buf, MAXBUF, stdin); //获取stdin里的可读数据存入buf if (!strncasecmp(buf, "quit", 4)) { printf("i will quit! "); break; } len = send(new_fd, buf, strlen(buf) - 1, 0); //send buf if (len > 0) printf ("send successful,%d byte send! ",len); else { printf("send failure!"); break; } } if (FD_ISSET(new_fd, &rfds)) //FD_ISSET 判断new_fd是否有可读数据 { bzero(buf, MAXBUF + 1); len = recv(new_fd, buf, MAXBUF, 0); //读取socket(new_fd)的可读数据 if (len > 0) printf ("recv success :'%s',%dbyte recv ", buf, len); else { if (len < 0) printf("recv failure "); else { printf("the ohter one end ,quit "); break; } } } } } close(new_fd); printf("need othe connecdt (no->quit)"); fflush(stdout); //清空stdout的缓冲区 bzero(buf, MAXBUF + 1); fgets(buf, MAXBUF, stdin); if (!strncasecmp(buf, "no", 2)) { printf("quit! "); break; } } close(sockfd); return 0; }
#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #define MAXBUF 1024 int main(int argc, char **argv) { int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF + 1]; fd_set rfds; struct timeval tv; int retval, maxfd = -1; if (argc != 3) { printf("argv format errno,pls: %s IP port ",argv[0], argv[0]); exit(EXIT_FAILURE); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(EXIT_FAILURE); } bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2])); if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(EXIT_FAILURE); } if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(EXIT_FAILURE); } printf(" get ready pls chat "); while (1) { FD_ZERO(&rfds); FD_SET(0, &rfds); FD_SET(sockfd, &rfds); maxfd = sockfd; tv.tv_sec = 1; tv.tv_usec = 0; retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("select %s", strerror(errno)); break; } else if (retval == 0) continue; else { if (FD_ISSET(sockfd, &rfds)) { bzero(buffer, MAXBUF + 1); len = recv(sockfd, buffer, MAXBUF, 0); if (len > 0) printf ("recv message:'%s',%d byte recv ",buffer, len); else { if (len < 0) printf ("message recv failure "); else { printf("the othe quit ,quit "); break; } } } if (FD_ISSET(0, &rfds)) { bzero(buffer, MAXBUF + 1); fgets(buffer, MAXBUF, stdin); if (!strncasecmp(buffer, "quit", 4)) { printf("i will quit "); break; } len = send(sockfd, buffer, strlen(buffer) - 1, 0); if (len < 0) { printf ("message send failure"); break; } else printf ("send success,%d byte send ",len); } } } close(sockfd); return 0; }
利用数组存放不确定数量的IO句柄fd判断其状态(数组全是-1,如果有句柄fd存放将改变状态(-1变成select的返回值),无数据时fd返回0,有数据时fd返回数据大小,而并非-1(error除外))
利用循环把数组里(要select的)的句柄加入到fd_set, 轮询过后再利用循环更新rfds
#include<stdio.h> #include<sys/types.h> #include<stdlib.h> #include<sys/socket.h> #include<sys/select.h> #include<netinet/in.h> #include<arpa/inet.h> #include<unistd.h> #include<string.h> #define _BACKLOG_ 5 //监听队列里允许等待的最大值 //当不确定有多少需要重载fd_set的fd时,把fd存入一个数组,方便循环重载 int fds[20];//用来存放需要处理的IO事件 int creat_sock(char *ip,char *port) { int sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0){ perror("creat_sock error"); exit(1); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(atoi(port)); local.sin_addr.s_addr = inet_addr(ip); if( bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){ perror("bind"); exit(2); } if(listen(sock,_BACKLOG_) < 0 ){ perror("listen"); exit(4); } return sock; } int main(int argc,char* argv[]) { if(argc != 3){ printf("Please use : %s [ip] [port] ",argv[0]); exit(3); } int listen_sock = creat_sock(argv[1],argv[2]); //创建socket对象 size_t fds_num = sizeof(fds)/sizeof(fds[0]); //获得数组长度 size_t i = 0; for(;i < fds_num;++i) //在socket里0也是句柄, 所以全部-1 , 以确定数组状态 { fds[i] = -1; } int max_fd = listen_sock; //确定最大fd select时+1 fd_set rset; //创建rfds while(1) { FD_ZERO(&rset); //置0 FD_SET(listen_sock,&rset); //把本地fd (加入重载)rfds max_fd = listen_sock; //struct timeval timeout = {20 , 0}; fds[0]=listen_sock; //确定第一个fd是本地直接加入 size_t i = 0; for(i=1;i < fds_num;++i) //从第二个开始加入fd { if(fds[i] > 0 ){ //大于0说明有fd FD_SET(fds[i] ,&rset); //把fd (加入重载)rfds if(max_fd < fds[i]) //冒泡算法,找出最大的fd { max_fd = fds[i]; } } } switch(select(max_fd+1,&rset,NULL,NULL,NULL)) //select最大fd+1 { case -1: perror("select"); break; case 0: printf("time out.. "); break; default: { size_t i = 0; for(;i < fds_num;++i) { //当为listen_socket事件就绪的时候,就表明有新的连接请求 if(FD_ISSET(fds[i],&rset) && fds[i] == listen_sock) //判断数组里的第一个fd(本地fd)是否有数据 { struct sockaddr_in client; int accept_sock = accept(listen_sock,(struct sockaddr*)&client,sizeof(client)); //接受连接并保存对端信息 if(accept_sock < 0){ perror("accept"); exit(5); } //char * paddr=NULL; //char * saddr=NULL; //paddr=inet_ntoa(client.sin_addr); //saddr=inet_ntoa(client.sin_addr); printf("connect by a client, ip:%s port:%d ",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); size_t i = 0; for(;i < fds_num;++i)//将新接受的描述符存入集合中 { if(fds[i] == -1){ fds[i] = accept_sock; break; } } if(i == fds_num) { close(accept_sock); } } //普通请求 else if(FD_ISSET(fds[i],&rset) && (fds[i] > 0)) { char buf[1024]; memset(buf,'