在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[];
if((fp = fopen("1.txt","w+")) == NULL)
{
perror("file open failure");
exit();
}
fwrite(buf , sizeof(buf), , fp);
memset(buff2,,sizeof(buff2));
fseek(fp,,SEEK_SET);
fread(buff2 ,sizeof(buff2) ,, fp);
printf(">>%s\n",buff2);
getchar();
fclose(fp);
return ;
}

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 + ];
fd_set rfds; //创建IO复用的容器
struct timeval tv; //select的超时时间
int retval, maxfd = -; if (argv[])
myport = atoi(argv[]); //输入端口
else
myport = ;
if (argv[])
lisnum = atoi(argv[]); //输入最大连接数,也可以NULL
else
lisnum = ;
if ((sockfd = socket(PF_INET, SOCK_STREAM, )) == -) { //创建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[])
my_addr.sin_addr.s_addr = inet_addr(argv[]); //输入本地地址
else
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -) { //绑定
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(sockfd, lisnum) == -) { //监听
perror("listen");
exit(EXIT_FAILURE);
} while ()
{
printf ("\n----wait for new connect\n");
len = sizeof(struct sockaddr);
if ((new_fd =accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -) { //接受连接并保存对端信息
perror("accept");
exit(errno);
} else
printf("server: got connection from %s, port %d, socket %d\n",
inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);
while ()
{
FD_ZERO(&rfds); //把IO复用的容器(rfds)置0
FD_SET(, &rfds); //把stdin加入到 rfds
FD_SET(new_fd, &rfds); //把new_fd加入到 rfds
maxfd = new_fd;
tv.tv_sec = ; //超时1秒
tv.tv_usec = ;
retval = select(maxfd + , &rfds, NULL, NULL, &tv); //阻塞 , 轮询加入到rfds里的fd ,超时后跳过阻塞往下执行
if (retval == -)
{
perror("select");
exit(EXIT_FAILURE);
} else if (retval == ) { //如果select返回0证明没有数据更新
continue; //跳过当前循环,强制开始下一次循环
}
else
{
if (FD_ISSET(, &rfds)) //FD_ISSET 判断stdin是否有可读数据
{
bzero(buf, MAXBUF + );
fgets(buf, MAXBUF, stdin); //获取stdin里的可读数据存入buf
if (!strncasecmp(buf, "quit", )) {
printf("i will quit!\n");
break;
}
len = send(new_fd, buf, strlen(buf) - , ); //send buf
if (len > )
printf ("send successful,%d byte send!\n",len);
else {
printf("send failure!");
break;
}
}
if (FD_ISSET(new_fd, &rfds)) //FD_ISSET 判断new_fd是否有可读数据
{
bzero(buf, MAXBUF + );
len = recv(new_fd, buf, MAXBUF, ); //读取socket(new_fd)的可读数据
if (len > )
printf ("recv success :'%s',%dbyte recv\n", buf, len);
else
{
if (len < )
printf("recv failure\n");
else
{
printf("the ohter one end ,quit\n");
break;
}
}
}
}
}
close(new_fd);
printf("need othe connecdt (no->quit)");
fflush(stdout); //清空stdout的缓冲区
bzero(buf, MAXBUF + );
fgets(buf, MAXBUF, stdin);
if (!strncasecmp(buf, "no", ))
{
printf("quit!\n");
break;
}
}
close(sockfd);
return ;
}

select_sever

#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 + ];
fd_set rfds;
struct timeval tv;
int retval, maxfd = -; if (argc != )
{
printf("argv format errno,pls:\n\t\t%s IP port\n",argv[], argv[]);
exit(EXIT_FAILURE);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, )) < )
{
perror("Socket");
exit(EXIT_FAILURE);
} bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(atoi(argv[]));
if (inet_aton(argv[], (struct in_addr *) &dest.sin_addr.s_addr) == )
{
perror(argv[]);
exit(EXIT_FAILURE);
} if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != )
{
perror("Connect ");
exit(EXIT_FAILURE);
} printf("\nget ready pls chat\n");
while ()
{
FD_ZERO(&rfds);
FD_SET(, &rfds);
FD_SET(sockfd, &rfds);
maxfd = sockfd;
tv.tv_sec = ;
tv.tv_usec = ;
retval = select(maxfd + , &rfds, NULL, NULL, &tv);
if (retval == -)
{
printf("select %s", strerror(errno));
break;
}
else if (retval == )
continue;
else
{
if (FD_ISSET(sockfd, &rfds))
{
bzero(buffer, MAXBUF + );
len = recv(sockfd, buffer, MAXBUF, );
if (len > )
printf ("recv message:'%s',%d byte recv\n",buffer, len);
else
{
if (len < )
printf ("message recv failure\n");
else
{
printf("the othe quit ,quit\n");
break;
}
}
}
if (FD_ISSET(, &rfds))
{
bzero(buffer, MAXBUF + );
fgets(buffer, MAXBUF, stdin);
if (!strncasecmp(buffer, "quit", )) {
printf("i will quit\n");
break;
}
len = send(sockfd, buffer, strlen(buffer) - , );
if (len < ) {
printf ("message send failure");
break;
} else
printf
("send success,%d byte send\n",len);
}
}
}
close(sockfd);
return ;
}

select_client

 利用数组存放不确定数量的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[];//用来存放需要处理的IO事件 int creat_sock(char *ip,char *port)
{
int sock = socket(AF_INET,SOCK_STREAM,);
if(sock < ){
perror("creat_sock error");
exit();
} 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)) < ){
perror("bind");
exit();
} if(listen(sock,_BACKLOG_) < ){
perror("listen");
exit();
} return sock;
} int main(int argc,char* argv[])
{
if(argc != ){
printf("Please use : %s [ip] [port]\n",argv[]);
exit();
}
int listen_sock = creat_sock(argv[],argv[]); //创建socket对象 size_t fds_num = sizeof(fds)/sizeof(fds[]); //获得数组长度
size_t i = ;
for(;i < fds_num;++i) //在socket里0也是句柄, 所以全部-1 , 以确定数组状态
{
fds[i] = -;
} int max_fd = listen_sock; //确定最大fd select时+1 fd_set rset; //创建rfds
while()
{
FD_ZERO(&rset); //置0
FD_SET(listen_sock,&rset); //把本地fd (加入\重载)rfds
max_fd = listen_sock;
//struct timeval timeout = {20 , 0};
fds[]=listen_sock; //确定第一个fd是本地直接加入
size_t i = ;
for(i=;i < fds_num;++i) //从第二个开始加入fd
{
if(fds[i] > ){ //大于0说明有fd
FD_SET(fds[i] ,&rset); //把fd (加入\重载)rfds
if(max_fd < fds[i]) //冒泡算法,找出最大的fd
{
max_fd = fds[i];
}
}
} switch(select(max_fd+,&rset,NULL,NULL,NULL)) //select最大fd+1
{
case -:
perror("select");
break;
case :
printf("time out..\n");
break;
default:
{
size_t i = ;
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 < ){
perror("accept");
exit();
}
//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\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); size_t i = ;
for(;i < fds_num;++i)//将新接受的描述符存入集合中
{
if(fds[i] == -){
fds[i] = accept_sock;
break;
}
}
if(i == fds_num)
{
close(accept_sock);
}
}
//普通请求
else if(FD_ISSET(fds[i],&rset) && (fds[i] > ))
{
char buf[];
memset(buf,'\0',sizeof(buf));
ssize_t size = read(fds[i],buf,sizeof(buf)-);
if(size < ){
perror("read");
exit();
}else if(size == ){
printf("client close..\n");
close(fds[i]);
fds[i] = -;
}else{
printf("client say: %s\n",buf);
} }
else{}
} }
break;
}
}
return ;
}

更优化的select使用


9.IO复用的内核实现      Linux应用层的阻塞都可能会被信号所中断(应用层的中断叫可中断睡眠状态)

  9.1 内核的实现其实是一个面向对象 步骤:

    

  面向对象三步骤: (回顾04所描述的 , Linux一切皆文件(内核设计的核心之一,链表: 利用结构体里的函数指针针对不同的需求,进行不同的分配(设置函数指针指向驱动(实现需求的)函数) , 再把对象注册(加入)到管理链表的函数里使用)完成面向对象设计的思路)

    注册对象: 内核里注册一个file_operations对象

    分配对象: 把结构体里的函数指针分别指向对应的函数驱动(等同于赋值吧)

    使用对象: 使用file提供的register(加入链表)函数, 把file_operations提交到file里

  应用层要使用select,通过一系列的查找,找到file---->file_operations------->select(select其实是调用了poll的驱动函数)逐层查找

  9.2: poll的实现和使用差不多(一般不会单独使用poll) 使用: https://blog.csdn.net/zhuxiaoping54532/article/details/51701549

               select 和 poll 和epoll的   区别: https://www.cnblogs.com/aspirant/p/9166944.html

  9.3 epoll的实现流程和使用 (IO复用大多数情况都是使用epoll了)

   select和poll受限于fd(数量有1000就影响效率了,并不能满足高并发的服务器), epoll将事件抽象成event(不受限于fd)

  epoll_even的结构:
struct epoll_event
{
uint32_t events;   /* 事件的类型 */
epoll_data_t data;    /* 事件的信息 */
} __attribute__ ((__packed__)); typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;

  一样是面向对象三步骤:

    创建epollfd对象

      int epoll_create(int size) //size = 监听事件的数量

      返回值 epoll_fd  , epoll_ event

    设置epollfd对象

      创建epoll_event对象

        struct epoll_event ep_ev  

      设置epoll_event对象

        ep_ev.events=(EPOLLIN 输入事件)(EPOLLOUT 输出事件)...  监听的事件类型,读取或者写入

        ep_ev.events.fd=listen_sock  监听句柄是否有事件产生

      使用:epoll_event对象 https://www.cnblogs.com/Dream-Chaser/p/7401184.html

        int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, epoll_event, ep_ev)
    使用epollfd对象
      int epoll_wait(int epfd, struct epoll_event* events,int maxevents, int timeout); events用来做返回的 http://blog.sina.com.cn/s/blog_488531130100i706.html
大白话的讲就是, 创建epoll_fd容器,里面放的是一个个的epoll_event事件, 事件对listen_sock监听,如果listen_sock有对客户端传入的数据进行读取(进而产生事件)
则进行一系列的返回, 而epoll_wait进行轮询(就是监听)也有超时(和select作用一样), epoll_ctl的作用是把事件加入到epoll_fd里
epoll使用的精髓  https://www.cnblogs.com/fnlingnzb-learner/p/5835573.html
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h> int creat_socket(char *ip,char* port)
{
int sock = socket(AF_INET,SOCK_STREAM,);
if(sock < ){
perror("socket");
exit();
} //调用setsockopt使当server先断开时避免进入TIME_WAIT状态,\
将其属性设定为SO_REUSEADDR,使其地址信息可被重用
int opt = ;
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)) < ){
perror("setsockopt");
exit();
} 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)) < ){
perror("bind");
exit();
} if(listen(sock,) < ){
perror("listen");
exit();
} printf("listen and bind succeed\n"); return sock;
} int set_noblock(int sock)
{
int fl = fcntl(sock,F_GETFL);
return fcntl(sock,F_SETFL,fl|O_NONBLOCK);
} int main(int argc,char *argv[])
{
if(argc != ){
printf("please use:%s [ip] [port]",argv[]);
exit();
}
int listen_sock = creat_socket(argv[],argv[]); //创建socket int epoll_fd = epoll_create(); //创建epoll_fd容器
if(epoll_fd < ){
perror("epoll creat");
exit();
} struct epoll_event ep_ev; //创建事件
ep_ev.events = EPOLLIN; //事件的类型 IN读取
ep_ev.data.fd = listen_sock; //产生事件的对象 listen_sock //添加关心的事件
//把事件和对象追加到epoll_fd
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&ep_ev) < ){
perror("epoll_ctl");
exit();
} struct epoll_event ready_ev[];//申请空间来放就绪的事件。
int maxnum = ;
int timeout = ;//设置超时时间,若为-1,则永久阻塞等待。
int ret = ; int done = ;
//把产生的事件放入ready_ev里,再循环处理每一个事件, 先判断事件是否为socket且是PEOLLIN, 如果是则把socket里的事件\
追加到epoll_fd中,退出当次循环,进行下一次循环. 其实就是把所以事件都当成普通IO进行处理,因为epoll_event里有fd所以可以使用\
recv read等读写函数
while(!done){
//阻塞: 轮询epoll_fd里的所有事件直到(fd对象)产生事件并返回事件数量,或者超时后返回0往下执行\
产生的事件会存放到ready_ev里
switch(ret = epoll_wait(epoll_fd,ready_ev,maxnum,timeout)){
case -:
perror("epoll_wait");
break;
case :
printf("time out...\n");
break;
default://至少有一个事件就绪
{
int i = ;
for(;i < ret;++i){ //循环处理epoll_fd里的事件
//判断是否为监听套接字,是的话accept
int fd = ready_ev[i].data.fd;
if((fd == listen_sock) && (ready_ev[i].events & EPOLLIN)){
struct sockaddr_in remote;
socklen_t len = sizeof(remote); int accept_sock = accept(listen_sock,(struct sockaddr*)&remote,&len);
if(accept_sock < ){
perror("accept");
continue;
}
printf("accept a client..[ip]: %s,[port]: %d\n",inet_ntoa(remote.sin_addr),ntohs(remote.sin_port));
//将新的事件添加到epoll集合中
ep_ev.events = EPOLLIN | EPOLLET;
ep_ev.data.fd = accept_sock; set_noblock(accept_sock); //设置成非阻塞 if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_sock,&ep_ev) < ){
perror("epoll_ctl");
close(accept_sock);
}
}
else{//普通IO
if(ready_ev[i].events & EPOLLIN){
//申请空间同时存文件描述符和缓冲区地址 char buf[];
memset(buf,'\0',sizeof(buf)); ssize_t _s = recv(fd,buf,sizeof(buf)-,);
if(_s < ){
perror("recv");
continue;
}else if(_s == ){
printf("remote close..\n");
//远端关闭了,进行善后
epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
close(fd);
}else{
//读取成功,输出数据
printf("client# %s",buf);
fflush(stdout); //将事件改写为关心事件,进行回写
ep_ev.data.fd = fd;
ep_ev.events = EPOLLOUT | EPOLLET; //在epoll实例中更改同一个事件
epoll_ctl(epoll_fd,EPOLL_CTL_MOD,fd,&ep_ev);
}
}else if(ready_ev[i].events & EPOLLOUT){
const char*msg = "HTTP/1.1 200 OK \r\n\r\n<h1> hi girl </h1>\r\n";
send(fd,msg,strlen(msg),);
epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
close(fd);
}
}
}
}
break; }
}
close(listen_sock);
return ;
}

tcp_epoll


总结: IO复用是一种机制,一个进程可以监听多个描述符,一旦某个描述符就绪(读就绪和写就绪),能够对程序进行相应的读写操作

  目前支持I/O复用的系统调用有select,poll,pselect,epoll,本质上这些I/O复用技术是同步I/O技术。一般都是使用epoll的

  与多进程和多线程相比,I/O复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

  select(): 创建fd_set结构体对象rfds(这是一个保存文件描述符的文件数组) , FD_ZERO(&rfds)置空rfds , FD_SET(fd, &rfds)文件描述符加入到rfds,

      select(nfds+1, &rfds, NULL, NULL, &timeout);轮询监听 rfds 里的文件状态是否改变, 返回状态改变的fd的数量, 无则返回0 超时, error返回-1

      FD_ISSET(fd, &rfds) 判断状态改变的fd是否是当前fd ,是则返回1 ,否则返回0 ,技巧:可以把fd放入一个数组通过下标 ,加入和判断

  epoll():  把监听fd状态的改变 ,转变成监听事件的发生  详细 https://www.cnblogs.com/fnlingnzb-learner/p/5835573.html

epoll_fd = epoll_create(size)    //创建事件容器和其大小
struct epoll_event ep_ev; //创建事件
ep_ev.events = EPOLLIN; //事件的类型   如果发生IN读取,则统一做出对应处理
ep_ev.data.fd = fd;       //产生事件的对象 ,如果产生事件的是一个socket_fd, 接受连接accept()后把其转化成事件,并加入rfds里
struct epoll_event ready_ev[128];        //申请空间来放就绪的事件。最大数不能超过创建epoll的大小
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,fd,&ep_ev);  //把fd和其事件类型追加到epoll_fd里
epoll_wait(epoll_fd,ready_ev,maxnum,timeout);  //等待事件的产生,类似于select()调用 ,把产生的事件依次存入ready_ev里 ,用循环[i]依次处理就绪事件
关于ET、LT两种工作模式: LT:只要内核缓冲区有数据就一直通知(一直触发),直到读完缓冲区, 这种模式可靠,但低效率
             ET:只有状态发生变化才通知 ,只触发一次(文件描述符状态变化时),可能会导致数据读不完 ,这种模式不可靠 ,但高效率,
                                            所以要自己实现一个能读取完整缓冲区的recv函数

    

Linux基础(06)IO复用的更多相关文章

  1. Linux中的IO复用接口简介(文件监视?)

    I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...

  2. Linux网络编程-IO复用技术

    IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...

  3. LINUX网络编程 IO 复用

    参考<linux高性能服务器编程> LINUX下处理多个连接时候,仅仅使用多线程和原始socket函数,效率十分低下 于是就出现了selelct poll  epoll等IO复用函数. 这 ...

  4. linux基础之IO模型

    一.IO模型 一次read操作: (1)等待数据准备好:从磁盘到内核内存 (2)从内核内存复制到进程内存 示意图如下: I/O类型: 同步和异步:synchronous,asynchronous 关注 ...

  5. java基础06 IO流

    IO用于在设备间进行数据传输的操作. Java IO流类图结构:   IO流分类 字节流: InputStream FileInputStream BufferedInputStream Output ...

  6. IO复用: select 和poll 到epoll

    linux 提供了select.poll和epoll三种接口来实现多路IO复用.下面总结下这三种接口. select 该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经 ...

  7. linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

      IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file ...

  8. 一只简单的网络爬虫(基于linux C/C++)————浅谈并发(IO复用)模型

    Linux常用的并发模型 Linux 下设计并发网络程序,有典型的 Apache 模型( Process Per Connection ,简称 PPC ), TPC ( Thread Per Conn ...

  9. Linux网络编程服务器模型选择之IO复用循环并发服务器

    在前面我们介绍了循环服务器,并发服务器模型.简单的循环服务器每次只能处理一个请求,即处理的请求是串行的,效率过低:并发服务器可以通过创建多个进程或者是线程来并发的处理多个请求.但是当客户端增加时,就需 ...

随机推荐

  1. haproxy 2.0 dataplaneapi rest api 转为graphql docker 镜像

    为了方便直接使用haproxy dataplaneapi graphql 格式的查询,制作了一个简单的docker 镜像 基于dotenv 进行配置管理,可以直接通过环境变量传入参数,处理不同hapr ...

  2. thymeleaf做if判定

    <div class="showing"> <h2>条件判断</h2> <p th:if="${testBoolean}&quo ...

  3. CSPS_114

    考前自闭赛 综合我100场血的教训,我的考试策略应该是: 1.不要期望能AC某道题,想都不要想,否则很容易直接崩 2.哪怕想到正解,先打暴力,把暴力码出来!没用也码! 稳扎稳打地得到代码难度最小的下一 ...

  4. CCF 201812-3 CIDR合并

    CCF 201812-3 CIDR合并 //100分 93ms #include<stdio.h>//CCF上stdio.h比cstdio快!!! #include<string.h ...

  5. 埃氏筛优化(速度堪比欧拉筛) + 洛谷 P3383 线性筛素数 题解

    我们一般写的埃氏筛消耗的时间都是欧拉筛的三倍,但是欧拉筛并不好想(对于我这种蒟蒻) 虽然 -- 我 -- 也可以背过模板,但是写个不会的欧拉筛不如写个简单易懂的埃氏筛 于是就有了优化 这个优化还是比较 ...

  6. python 获取文件运行路径

    import os print(os.getcwd()) print("/".join(os.path.dirname(os.path.abspath(__file__)).spl ...

  7. RAdam VS Adam

    论文解读:Radam:ON THE VARIANCE OF THE ADAPTIVE LEARNING RATE AND BEYOND 1,目的 想找到一个比较好的优化器,能够在收敛速度和收敛的效果上 ...

  8. LSF 作业系统常用命令

    LSF(Load Sharing Facility)是一个被广泛使用的作业管理系统,具有高吞吐.配置灵活的优点.通过 LSF 集中监控和调度,可以充分利用计算机的CPU.内存.磁盘等资源. bqueu ...

  9. Java编程思想之三 操作符

    在底层,Java中的数据是通过使用操作符来操作的. 3.2 使用Java操作符 操作符接收一个或多个参数,并生成一个新值. 操作符作用于操作数,生成一个新值.有些操作符可能会改变操作数自身的值,这被称 ...

  10. 如何查看电脑的GPU信息

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_33690342/article/ ...