1.阻塞IO
CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
2.非阻塞IO
能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转
fcntl(fd,F_GETFL);获得文件描述符的属性
flags |= O_NONBLOCK;在现有属性中加入非阻塞属性
fcntl(fd,F_SETFL,flags);将新属性设置回文件描述符
#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());
#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); void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); |
练习:
#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
#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; }