1.阻塞IO
CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
2.非阻塞IO
能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转
fcntl(fd,F_GETFL);获得文件描述符的属性
flags |= O_NONBLOCK;在现有属性中加入非阻塞属性
fcntl(fd,F_SETFL,flags);将新属性设置回文件描述符
cpp
#include "head.h"
int main(void)
{
int fd = 0;
char tmpbuff[4096] = {0};
int flags;
ssize_t nsize = 0;
char *pret = NULL;
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
/* 获得fd文件描述符的属性 */
flags = fcntl(fd, F_GETFL);
/* 在现有属性中加入非阻塞属性 */
flags |= O_NONBLOCK;
/* 将新属性设置回fd文件描述符 */
fcntl(fd, F_SETFL, flags);
flags = fcntl(0, F_GETFL);
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = read(fd, tmpbuff, sizeof(tmpbuff));
if (nsize > 0)
{
printf("FIFO:%s\n", tmpbuff);
}
memset(tmpbuff, 0, sizeof(tmpbuff));
pret = gets(tmpbuff);
if (NULL != pret)
{
printf("STDIN:%s\n", tmpbuff);
}
}
close(fd);
return 0;
}
3.异步IO
将一个文件描述符设定为异步IO,当IO有事件发生时,内核会向用户层发送SIGIO信号提醒用户层处理事件
signal(SIGIO,handle);
flag = fcntl(fd,F_GETFL);
flag |= O_ASYNC;//将fd设置为异步IO(文件描述符,发生可读事件时,会发送信号通知)
fcntl(fd,F_SETFL,flag);//把异步IO事件的接收进程设置为当前进程,通知给当前进程
fcntl(fd,F_SETOWN,getpid());
cpp
#include "head.h"
int fd = 0;
void handler(int signo)
{
char tmpbuff[4096] = {0};
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("RECV:%s\n", tmpbuff);
return;
}
int main(void)
{
char tmpbuff[4096] = {0};
int flags;
signal(SIGIO, handler);
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
flags = fcntl(fd, F_GETFL);
flags |= O_ASYNC;
//将fd设置为异步IO(文件描述符发生可以读的事件,会发送信号通知)
fcntl(fd, F_SETFL, flags);
//通知给当前进程
fcntl(fd, F_SETOWN, getpid());
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);
}
close(fd);
return 0;
}
4.多路复用IO
select
监听文件描述符集合,将所有要监听的事件加入集合中,使用select监听所有事件,当集合中有事件发生, select不再阻塞,同时select会将产生事件的文件描述符留在集合中,而把没有产生事件的文件描述符从集合中踢出,所以留在集合中的文件描述即为产生事件的文件描述符,对其处理即可
注意:因为select会把没有产生事件的文件描述符从集合中踢出,所以要用一个临时变量tmp来使用select
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 功能: 监听文件描述符是否有事件发生 参数: nfds:最大文件描述符的值 + 1 readfds:读文件描述符集合 writefds:写文件描述符集合 exceptfds:异常文件描述符集合 timeout:超时时间 返回值: 成功返回产生事件的文件描述符个数 失败返回-1 void FD_CLR(int fd, fd_set *set); 功能:将fd从集合中清除 int FD_ISSET(int fd, fd_set *set); 功能:判断fd是否仍在文件描述符集合中 void FD_SET(int fd, fd_set *set); 功能:将fd加入文件描述符集合中 void FD_ZERO(fd_set *set); 功能:将文件描述符集合清0 |
练习:
cpp
#include "head.h"
int CreateTcpConnection(const char *pip, int port)
{
int sockfd = 0;
int ret = 0;
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(port);
seraddr.sin_addr.s_addr = inet_addr(SER_IP);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
return -1;
}
ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
return -1;
}
return sockfd;
}
int HandleConnection(int sockfd)
{
char tmpbuff[4096] = {0};
static int cnt = 0;
ssize_t nsize = 0;
sprintf(tmpbuff, "hello world --- %d", cnt);
nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
if (-1 == nsize)
{
return -1;
}
cnt++;
memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
if (-1 == nsize)
{
return -1;
}
else if (0 == nsize)
{
return 0;
}
printf("RECV:%s\n", tmpbuff);
return nsize;
}
int main(void)
{
int sockfd = 0;
int ret = 0;
sockfd = CreateTcpConnection(SER_IP, SER_PORT);
if (-1 == sockfd)
{
printf("连接服务器异常\n");
return -1;
}
while (1)
{
ret = HandleConnection(sockfd);
if (-1 == ret)
{
printf("连接出错!\n");
break;
}
else if (0 == ret)
{
printf("连接关闭\n");
break;
}
sleep(1);
}
close(sockfd);
return 0;
}
server.c
cpp
#include "head.h"
int CreateListenSocket(const char *pip, int port)
{
int sockfd = 0;
int ret = 0;
struct sockaddr_in seraddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
return -1;
}
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(port);
seraddr.sin_addr.s_addr = inet_addr(pip);
ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
return -1;
}
ret = listen(sockfd, 10);
if (-1 == ret)
{
return -1;
}
return sockfd;
}
int HandleConnection(int confd)
{
char tmpbuff[4096] = {0};
ssize_t nsize = 0;
nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
if (-1 == nsize)
{
return -1;
}
else if (0 == nsize)
{
return 0;
}
printf("RECV:%s\n", tmpbuff);
sprintf(tmpbuff, "%s --- echo", tmpbuff);
nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
if (-1 == nsize)
{
return -1;
}
return nsize;
}
int main(void)
{
int sockfd = 0;
int confd = 0;
int ret = 0;
fd_set rdfds;
fd_set tmpfds;
int nready = 0;
int maxfd = 0;
int i = 0;
//创建监听套接字
sockfd = CreateListenSocket(SER_IP, SER_PORT);
if (-1 == sockfd)
{
printf("创建监听套接字失败\n");
return -1;
}
//将sockfd加入监听集合中
FD_ZERO(&rdfds);
FD_SET(sockfd, &rdfds);
maxfd = sockfd;
while (1)
{
//开始监听
tmpfds = rdfds;
nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
if (-1 == nready)
{
perror("fail to select");
return -1;
}
//如果sockfd产生事件,处理新的连接请求,并将新的文件描述符加入集合,下次一起监听
if (FD_ISSET(sockfd, &tmpfds))
{
confd = accept(sockfd, NULL, NULL);
if (-1 == confd)
{
FD_CLR(sockfd, &rdfds);
close(sockfd);
continue;
}
maxfd = maxfd > confd ? maxfd : confd;
FD_SET(confd, &rdfds);
}
//遍历所有已经连接的客户端中是否有事件发生
for (i = sockfd+1; i <= maxfd; i++)
{
if (FD_ISSET(i, &tmpfds))
{
ret = HandleConnection(i);
if (-1 == ret)
{
printf("连接异常\n");
FD_CLR(i, &rdfds);
close(i);
continue;
}
else if (0 == ret)
{
printf("连接断开\n");
FD_CLR(i, &rdfds);
close(i);
continue;
}
}
}
}
close(sockfd);
return 0;
}