IO多路复用之select

来源:互联网 发布:大量收购淘宝买家信息 编辑:程序博客网 时间:2024/06/05 04:07

前言:

select系统调用的用途是:在指定的一段时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。

select API:

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

FD_ZERO(fd_set *fd_set);          //清除fdset的所有位

FD_SET(int fd, fd_set *fd_set);  //设置fdset的位fd

FD_CLR(int fd, fd_set *fd_set): //清除fdset的位fd

int FD_ISSET(int fd, fd_set *fdset); //测试fdset的位fd是否被设置(文件描述符fd是否就绪)

文件描述符的就绪条件:

--可读

1、socket内核接收缓冲区中的字节数大于或等于其低水位标记SO_RCVLOWAT。此时我们可以无阻塞地读取该文件描述符,并且读操作返回的字节数大于0。

2、socket通信的对方关闭连接。此时对该socket的读操作将返回0。

3、socket上有未处理的错误。

--可写

1、socket内核发送缓冲区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT,此时我们可以无阻塞地写该socket,并且写操作返回的字节数大于0。

2、socket的写操作被关闭。

3、socket使用非阻塞connect连接成功或者失败(超时)之后。

4、socket上有未处理的错误。

编码实例

服务端select_s.c

/* *单进程IO多路复用select模型 * */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/select.h>#include <unistd.h>#include <sys/types.h>#define IPADDRESS   "127.0.0.1"#define PORT        8787#define MAXLINE     1024#define LISTENQ     5//创建套接字并进行绑定static int socket_bind(const char* ip,int port);//IO多路复用selectstatic void do_select(int listenfd);//处理多个连接static void handle_connection(int *connfds,int num,fd_set *prset,fd_set *pallset);int main(int argc,char *argv[]){    int  listenfd,connfd,sockfd;    struct sockaddr_in cliaddr;    socklen_t cliaddrlen;    listenfd = socket_bind(IPADDRESS,PORT);    listen(listenfd,LISTENQ);    do_select(listenfd);    return 0;}static int socket_bind(const char* ip,int port){    int  listenfd;    struct sockaddr_in servaddr;    listenfd = socket(AF_INET,SOCK_STREAM,0);    if (listenfd == -1)    {        perror("socket error:");        exit(1);    }    bzero(&servaddr,sizeof(servaddr));    servaddr.sin_family = AF_INET;    inet_pton(AF_INET,ip,&servaddr.sin_addr);    servaddr.sin_port = htons(port);    if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)    {        perror("bind error: ");        exit(1);    }    return listenfd;}static void do_select(int listenfd){    int  connfd,sockfd;    struct sockaddr_in cliaddr;    socklen_t cliaddrlen;    fd_set  rset,allset;    int maxfd,maxi;    int i;    int clientfds[FD_SETSIZE];  //保存客户连接描述符    int nready;    //初始化客户连接描述符    for (i = 0;i < FD_SETSIZE;i++)        clientfds[i] = -1;   maxi = -1;    FD_ZERO(&allset);    //添加监听描述符    FD_SET(listenfd,&allset);    maxfd = listenfd;    //循环处理    for ( ; ; )    {        rset = allset;        //获取可用描述符的个数        nready = select(maxfd+1,&rset,NULL,NULL,NULL);        if (nready == -1)        {            perror("select error:");            exit(1);        }        //测试监听描述符是否准备好,如果就绪的是listenfd,则接收新连接。        if (FD_ISSET(listenfd,&rset))        {            cliaddrlen = sizeof(cliaddr);            //接收新的连接            if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)            {                if (errno == EINTR)                    continue;                else                {                   perror("accept error:");                   exit(1);                }            }            fprintf(stdout,"accept a new client: %d:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);            //将新的连接描述符添加到数组中            for (i = 0;i <FD_SETSIZE;i++)            {                if (clientfds[i] < 0)                {                    clientfds[i] = connfd;                    break;                }            }            if (i == FD_SETSIZE)            {                fprintf(stderr,"too many clients.\n");                exit(1);            }            //将新的描述符添加到读描述符集合中            FD_SET(connfd,&allset);            //描述符个数            maxfd = (connfd > maxfd ? connfd : maxfd);            //记录客户连接套接字的个数            maxi = (i > maxi ? i : maxi);            if (--nready <= 0)                continue;        }        //如果不是连接监听套接字,则处理已连接客户请求        handle_connection(clientfds,maxi,&rset,&allset);    }}static void handle_connection(int *connfds,int num,fd_set *prset,fd_set *pallset){    int i,n;    char buf[MAXLINE];    memset(buf,0,MAXLINE);    for (i = 0;i <= num;i++)    {        if (connfds[i] < 0)            continue;        //测试客户描述符是否准备好,即是否有新的消息可读。        if (FD_ISSET(connfds[i],prset))        {            //接收客户端发送的信息            n = read(connfds[i],buf,MAXLINE);            if (n == 0)            {                close(connfds[i]);                FD_CLR(connfds[i],pallset);                connfds[i] = -1;                continue;            }            printf("read msg is: ");            write(STDOUT_FILENO,buf,n);            //向客户端发送buf            write(connfds[i],buf,n);        }    }}

客户端select_c.c

#include <netinet/in.h>#include <sys/socket.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/select.h>#include <time.h>#include <unistd.h>#include <sys/types.h>#define MAXLINE     1024#define IPADDRESS   "127.0.0.1"#define SERV_PORT   8787#define max(a,b) (a > b) ? a : bstatic void handle_connection(int sockfd);int main(int argc,char *argv[]){    int                 sockfd;    struct sockaddr_in  servaddr;    sockfd = socket(AF_INET,SOCK_STREAM,0);    bzero(&servaddr,sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(SERV_PORT);    inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);    connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));    //处理连接描述符    handle_connection(sockfd);    return 0;}static void handle_connection(int sockfd){    char    sendline[MAXLINE],recvline[MAXLINE];    int     maxfdp,stdineof;    fd_set  rset;    int n;    FD_ZERO(&rset);    for (; ;)    {        //添加标准输入描述符        FD_SET(STDIN_FILENO,&rset);        //添加连接描述符        FD_SET(sockfd,&rset);        maxfdp = max(STDIN_FILENO,sockfd);        //进行轮询        select(maxfdp+1,&rset,NULL,NULL,NULL);        //测试连接套接字是否准备好        if (FD_ISSET(sockfd,&rset))        {            n = read(sockfd,recvline,MAXLINE);            if (n == 0)            {                    fprintf(stderr,"client: server is closed.\n");                    close(sockfd);                    FD_CLR(sockfd,&rset);            }            write(STDOUT_FILENO,recvline,n);        }        //测试标准输入是否准备好        if (FD_ISSET(STDIN_FILENO,&rset))        {            n = read(STDIN_FILENO,sendline,MAXLINE);            if (n  == 0)            {                FD_CLR(STDIN_FILENO,&rset);                continue;            }            write(sockfd,sendline,n);        }    }}



0 0
原创粉丝点击