阻塞io模式
阻塞模式 :需要等待一个功能完成了 才能到下一个实现
比如 write read 举例 一句话 概括 读写不可以同时进行 也就是顺序执行
就很好体现出阻塞模式的说法了 要么在读的时候 写在等待 相反也是
但是如果写一直不结束 或者 读出来的数据 是空 的
这样的就会永远发生阻塞了 就会哞哞不动了
非阻塞:不需要询问是否同意,不需要等待可以直接运行下一个工作。
第一点和第二点:
如果让一个设置为非阻塞通信模式的函数 等待 那就会出错 让它返回一个错误信息。
非阻塞目标地址的数据会被测试 是否是可读的 也就是 存不存在数据
第三 :如果不停对 cpu 发起读写 很消耗他的资源 。
io多路复用 顾名思义 就是 用一个io 重重复复 用了 几次
也就是 实际存在的东西只有一个 但是 映射出来了好几个 内存只会占一个
基本思路
1 他会把函数地址Z暂时存入R14 寄存器 到了函数返回 再从R14取出 地址
最后到了函数点 执行 (不懂的 看我博客的《上电到main.C的过程》)
(前)先有地址 (后 )才可以存数据 存了就可以读
实现的框架
实现目标作业
1 实现 服务器 和客户端 全双工模式
写的同时 可以读到数据 并且对方可以接收到数据
服务器只能作为 读取 或者发送中转站
大概的框架 是这样的
服务器的大圈 中 的小圈 表示 write 只能为一个中转站
也就是 客户端 用间接方式 读了他写的数据
1客户端是 间接读取它的数据 和本来就是具有写的操作
2 最后一个参数是时间的参数 用于规定时间 超了就是异常
3除了圈之外 全部设置为NULL
在多线程的基础上改
客户端
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
int fd =-1;
struct sockaddr_in sin;
if(argc!=3)
{
exit(1);
}
int port =atoi(argv[2]);
//XXXX 192.186.2.129 5003
fd=socket(AF_INET,sock_stream,0);
if(fd<0)
{
perror("socket");
exit(1);
}
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(port);
sin.sin_addr.s_addr=inet_addr(argv[1]);
//XXXX 192.186.2.129 5003
if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
perror("connect");
exit(1);
}
fd_set rest=-1;
int maxfd=-1;
struct timeval tout;
char buf[1024];
int rts=-1;
while(1)
{
FD_ZERO(&rest);
FD_SET(0,&rest); //0开始; //0----stdin (标准io模式)
FD_SET(fd,&rest);//最大可到1024;
tout.tv_sec=5; //5秒
tout.tv_usec=5; //0微妙 0-5的区间判断
maxfd=fd;//下标 所以要加一
select(maxfd+1,&rest,NULL,NULL,&tout);//观察前后数据变化
//0----stdin (标准io)
if(FD_ISSET(0,&rest)) //对应集合 FD_SET(0,&rest);//最大可到1024;
{
bzero(buf,1024);
do
{
rts=read(0,buf,1023);
}while(rts<0);
if(rts<0)
{
continue;
}
if(!rts)break;
if(write(fd,buf,strlen(buf))<0)
{
break;
}
if(strncasecmp(buf,"QUIT",strlen("QUIT")))//输入退出
{
break;
}
}
if(FD_ISSET(fd,&rest)) //对应集合 FD_SET(fd,&rest);//最大可到1024;
{
bzero(buf,1024);
do
{
rts=read(fd,buf,1023);
}while(rts<0);
if(rts<0)
{
continue;
}
if(!rts)break;
if(write(fd,buf,strlen(buf))<0)
{
break;
}
printf("seivel saiy is %s\n",buf);
if(strncasecmp(buf,"QUIT",strlen("QUIT")))//输入退出
{
break;
}
}
}
return 0;
}
服务器
#include
#include
#include
#include
#include
#include
#include
#include
void child(void *arg);//声明
void kill_wait(int num)
{
if(num==SIGCHLD)
{
waitpid(-1,NULL,WNOHANG);
}
}
int main()
{
int fd =-1;
struct sockaddr_in sin;
fd=socket(AF_INET,sock_stream,0);
kill_wait(SIGCHID);
if(fd<0)
{
perror("socket");
exit(1);
}
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(5002);
// sin.sin_addr.s_addr=inet_addr("192.168.2129");
sin.sin_addr.s_addr=INADDR_INY;
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
perror("bind");
exit(1);
}
if(listen(fd,5)<0)
{
perror("listen");
exit(1);
}
int newfd=-1;
char buf[1024];
struct sockaddr_in cin;
socklen len=sizeof(cin);
pid_t pid;
while(1)
{
newfd=accept(fd,(struct sockaddr* )&cin,&len);//&ci哪里都存有端口号和 i 地址了但是现在为网络字节序 需要转为主机字节序
if(newfd<0)
{
perror("accept");
exit(1);
}
char ipv4_buf[16];//比如 192.168.2.129+'/' ==14个字符 所以数组不要过多大这个大小
if(!inet_ntop(AF_INET,(void *)&cin.sin_addr.s_addr,ipv4_buf,sizeof(cin)))
{
perror("inet_ntop");
exit(1);
}
printf("IPV4=====%s port====%d\n",ipv4_buf,ntohs(cin.sin_port));
//ntohs函数网络字节序转为主机字节序
pid=fork();
if(pid<0)
{
break;
}
if(pid==0)
{
close(fd);
child(&newfd);// 子进程工作
}
if(pid>0)
{
close(newfd);
}
}
close(fd);
close(newfd);
return 0;
}
void child(void *arg)
{
char buf[1024];
int rts=-1;
int newfd=*(int *)arg;
int ws=-1;
while(1)
{
do
{
bzero(buf,1024);
rts=read(newfd,buf,1023);
}while(rts<1);//while(rts<1);==rts>1
if(rts<0)
{
exit(1);
}
if(rts==0)
{
break;
}
printf("read is %s\n",buf);
do //中转站 发送回去
{
ws=write(newfd,buf,strlen(buf));
}while(ws<0);
if(strncasecmp(buf,"QUIT",strlen("QUIT")))//输入退出
{
break;
}
}
close(fd);
close(newfd);
}
客户端输入"QUIT" 退出 到命令行