IO多路复用 select与poll
来源:互联网 发布:iphone蜂窝数据设置 编辑:程序博客网 时间:2024/06/05 15:11
1. 阻塞与非阻塞
阻塞方式block,进程执行到这一函数时必须等待事件发生,如果没发生,就一直阻塞函数不能返回
非阻塞 non-block
进程或线程执行不必等待事件发生一旦执行肯定返回以不停返回返回值来反应函数执行情况
Select就是这样监视描述符的变化
2.select模型
两结构体:
Structfd_set存放描述符,文件句柄的聚合
建立文件联系四个宏
FD_ZERO(fd_set*fd_set);//清空fdset与所有文件句柄的联系
FD_SET(intfd,fd_set *fdset);//建立文件句柄与fdset的联系
FD_CLR(intfd,fd_set *fdset); 清除文件句柄fd与fdset的联系
FD_ISSET(intfd,fd_set*fdset);//检查fdset联系的文件句柄fd是否读写,>0表示可读写
Structtimeval 两个成员 tv_sec表示秒数,tv_usec表示毫秒数
Select函数原型
Int select(intnfds,fd_set *rdfds,fd_set *wtfds,fd_set *exfds,struct timeval *timeout);
A.其中rdfds(wtfds)读集,度过有一个文件可读,select返回一个大于0的值表示文件可读超出timeout时间select返回0,发生错误返回负值,
可传入NULL表示不关心文件读写变化
Exfds异常文件集有特殊情况发生select返回
B.timeval*out 若果设置为NULL变为阻塞方式
设置为0完全非阻塞
C.返回值大于0文件可读或可写
返回值为0,out of time
返回值小于0,select错误
network.h
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <signal.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#define ERR_EXIT(m) \ do { \ perror(m);\ exit(EXIT_FAILURE);\ }while(0)ssize_t readn(int fd, void *usrbuf, size_t n);ssize_t writen(int fd, void *usrbuf, size_t n);ssize_t recv_peek(int sockfd, void *usrbuf, size_t n);ssize_t readline(int sockfd, void *usrbuf, size_t maxline);
network.c
#include "network.h"ssize_t readn(int fd, void *usrbuf, size_t n){ size_t nleft = n; //表示还需要读取的字节数 ssize_t nread; char *bufp = usrbuf; //控制read函数存放的位置 while(nleft > 0) { if((nread = read(fd, bufp, nleft)) == -1) { if(errno == EINTR) //interupt nread = 0; //continue; 中断需要再次读取 else return -1; // ERROR }else if(nread == 0) // EOF break; nleft -= nread; bufp += nread; } return (n - nleft);}ssize_t writen(int fd, void *usrbuf, size_t n){ size_t nleft = n; ssize_t nwrite; char *bufp = usrbuf; while(nleft > 0) { //nwrite == 0也属于错误 if((nwrite = write(fd, bufp, nleft)) <= 0) { if(errno == EINTR) nwrite = 0; else return -1; // -1 和 0 } nleft -= nwrite; bufp += nwrite; } return n; //这里不是 n- nleft 必须是n}//recv_peek选项完成一次正确的读取过程。ssize_t recv_peek(int sockfd, void *buf, size_t len) { int nread; while (1) { //这个过程只成功调用一次 nread = recv(sockfd, buf, len, MSG_PEEK); if (nread < 0 && errno == EINTR) { //被中断则继续读取 continue; } if (nread < 0) { return -1; } break; } return nread;}ssize_t readline(int sockfd, void *buf, size_t maxline) { int nread; //一次IO读取的数量 int nleft; //还剩余的字节数 char *ptr; //存放数据的指针的位置 int ret; //readn的返回值 int total = 0; //目前总共读取的字节数 nleft = maxline-1; ptr = buf; while (nleft > 0) { //这一次调用仅仅是预览数据 //并没有真的把数据从缓冲区中取走 ret = recv_peek(sockfd, ptr, nleft); //注意这里读取的字节不够,绝对不是错误!!! if (ret <= 0) { return ret; } nread = ret; int i; for (i = 0; i < nread; ++i) { if (ptr[i] == '\n') { //这里才是真正的读取过程 ret = readn(sockfd, ptr, i + 1); if (ret != i + 1) { return -1; } total += ret; ptr += ret; *ptr = 0; return total; //返回此行的长度 '\n'包含在其中 } } //如果没有发现\n,这些数据应全部接收 ret = readn(sockfd, ptr, nread); if (ret != nread) { return -1; } nleft -= nread; total += nread; ptr += nread; } *ptr = 0; return maxline-1;}client.c
#include "network.h"#include <sys/select.h>#include <sys/epoll.h>void do_service(int peerfd){ char recvbuf[1024]={0}; char sendbuf[1024]={0}; //init fd_set readset; FD_ZERO(&readset); //防止重定向不用STDIN_FILENO int stdin_fd=fileno(stdin); int maxfd=(stdin_fd>peerfd)?stdin_fd:peerfd; int nready; while(1) { FD_ZERO(&readset); FD_SET(stdin_fd,&readset); FD_SET(peerfd,&readset); nready=select(maxfd+1,&readset,NULL,NULL,NULL); if(nready==-1) { if(errno==EINTR) continue; ERR_EXIT("select"); } if(FD_ISSET(stdin_fd,&readset)) { fgets(sendbuf,1024,stdin); writen(peerfd,sendbuf,strlen(sendbuf)); } if(FD_ISSET(peerfd,&readset)) { int ret= readline(peerfd,recvbuf,1024); if(ret==-1) { ERR_EXIT("readline"); } else if(ret==0) { shutdown(peerfd,SHUT_WR); return; } printf("recv data : %s",recvbuf); } } }int main(int argc, const char *argv[]){ int peerfd=socket(AF_INET,SOCK_STREAM,0); if(peerfd==-1) ERR_EXIT("socket"); struct sockaddr_in peeraddr; peeraddr.sin_family=AF_INET; peeraddr.sin_port=htons(8989); peeraddr.sin_addr.s_addr=inet_addr("127.0.0.1"); if(connect(peerfd,(struct sockaddr *)&peeraddr,sizeof(peeraddr))==-1) ERR_EXIT("CONNECT"); do_service(peerfd); close(peerfd); return 0;}
1.使用select编写客户端程序的一般步骤:
初始化参数,包括readset、maxfd、nready
while(1)
{
1.先重新设置readset
2.执行select调用,包括检查返回值
3.依次检查stdin和peerfd,如果是前者就从键盘读取数据,如果是后者,就使用readline接收网络数据。
}
3.poll模型
client.c
#include "network.h"#include <poll.h>void do_service(int peerfd){ char recvbuf[1024]={0}; char sendbuf[1024]={0}; //init struct pollfd client[2];//listen 2 fd client[0].fd=fileno(stdin); client[0].events =POLLIN; //listen read events client[1].fd=peerfd; client[1].events=POLLIN; int maxi =1; //max subscript of array client[1] int nready;// join poll echo value while(1) { // int poll(struct pollfd *fds,nfds_t nfds,int timeout); nready=poll(client,maxi+1,-1); // -1 said permanent waiting if(nready==-1) { if(errno==EINTR) continue; ERR_EXIT("poll"); } else if(nready ==0) { continue; } //check stdin and peerfd according to priority if(client[0].revents & POLLIN) { if(fgets(sendbuf,1024,stdin)==NULL) { shutdown(peerfd,SHUT_WR); client[0].fd=-1; //do not listen stdin; } else { writen(peerfd,sendbuf,strlen(sendbuf)); } } if(client[1].revents&POLLIN) { int ret=readline(peerfd,recvbuf,1024); if(ret==-1) ERR_EXIT("readline"); else if(ret==0) { printf("server close\n"); close(peerfd); break; } printf("recv data: %s",recvbuf); } }}int main(int argc, const char *argv[]){ int peerfd=socket(AF_INET,SOCK_STREAM,0); if(peerfd==-1) ERR_EXIT("socket"); struct sockaddr_in peeraddr; peeraddr.sin_family=AF_INET; peeraddr.sin_port=htons(8989); peeraddr.sin_addr.s_addr=inet_addr("127.0.0.1"); if(connect(peerfd,(struct sockaddr *)&peeraddr,sizeof(peeraddr))==-1) ERR_EXIT("CONNECT"); do_service(peerfd); close(peerfd); return 0;}
使用poll编写客户端的一般步骤:
准备数组(这里为2),填入相应的fd以及events。还有maxi、nready
while(1)
{
1.执行poll,以及检查返回值
2.检查两个fd,通过revents字段
}
- IO多路复用 select与poll
- 多路复用IO&select&poll
- 多路复用select与poll
- 多路复用select与poll
- 多路复用select与poll
- 多路复用select与poll
- 多路复用select与poll
- IO多路复用(select poll epoll)
- IO多路复用:select、poll、epoll
- IO多路复用之select poll epoll
- IO多路复用之select、poll以及epoll
- IO多路复用之select、poll、epoll详解
- IO 多路复用之 select、poll、epoll 详解
- IO多路复用:select、poll、epoll示例
- io多路复用之select,poll,epoll总结
- IO多路复用 --select、poll、epoll模式
- IO多路复用select,poll,epoll的区别
- IO多路复用之select、poll、epoll详解
- 线段树hdu4893(多校联合)
- plsql启动时无法解析指定的连接标识符
- 在 mac 上使用 Git 和 GitHub 连接
- poj 2420 A Star not a Tree?
- 【布局相关】RelativeLayout用到的一些重要的属性
- IO多路复用 select与poll
- HDU 4893(Wow! Such Sequence!)
- 搜索专题
- Django学习笔记(二)Django使用template
- 高级for循环在java集合中的应用
- 【leetcode】Balanced Binary Tree
- 关于easyui通过href引入外部jsp页面js不执行的说明
- Ununtu下apt-fast安装
- Linux 2.6 内核阅读笔记 内存管理