Linux select机制

select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型: 
#include <sys/time.h> 
#include <unistd.h> 
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); 
参数maxfd是需要监视的最大的文件描述符值+1;
参数rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合;
参数struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0;
返回值:返回对应位仍然为1的fd的总数。三组fd_set均将某些fd位置0,只有那些可读,可写以及有异常条件待处理的fd位仍然为1。

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化。
FD_SET(int fd, fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。 
FD_CLR(int fd, fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。 
FD_ISSET(int fd, fd_set *fdset);用于测试指定的文件描述符是否在该集合中。         
      
timeout用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。有三种可能:
1.timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件。
2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
3.timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生

读取键盘输入实例

#include<sys/time.h>  
#include<sys/types.h>  
#include<unistd.h>  
#include<string.h>  
#include<stdlib.h>  
#include<stdio.h>  
int main()  
{  
     char buf[10]="";  
     fd_set rdfds;  
     struct timeval tv;  
     int ret;  
     FD_ZERO(&rdfds);    //清空的rdfs文件集合
     FD_SET(0,&rdfds);   //将标准输入加入到rdfds
  
     while(1)
     {     
        ret = select(1,&rdfds,NULL,NULL,NULL);//阻塞等待用户输入
        if(ret<0)  
            printf("selcet failed\n");  
        else if(ret == 0)  
            printf("seletc timeout\n");  
        else  
            printf("ret = %d\n",ret);  
        if(FD_ISSET(0,&rdfds))    //测试标准输入是否可读
        {  
            printf("reading data\n");  
            fread(buf,9,0,stdin); //从标准输入中读取数据
        }  
        write(1,buf,strlen(buf));//将输入的数据在屏幕显示出来
     }
        return 0;  
}                                                                                                                                                         

web服务器实例

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <errno.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
  
#define MYPORT 88960    // the port users will be connecting to  
#define BACKLOG 10      // how many pending connections queue will hold  
#define BUF_SIZE 200  

int fd_A[BACKLOG];    // accepted connection fd  
int conn_amount;    // current connection amount  

int main(void)  
{  
    int sock_fd, new_fd;              //sock_fd监听套接字, new_fd新连接的套接字 
    struct sockaddr_in server_addr;   //服务端地址信息
    struct sockaddr_in client_addr;   //客户端地址信息
    socklen_t sin_size;  
    int yes = 1;  
    char buf[BUF_SIZE];  
    int ret;  
    int i;  
  
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)//新建监听套接字
    {  
        perror("socket error!\n");  
        exit(1);  
    }  
  
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) 
    {  
        perror("setsockopt");  
        exit(1);  
    }  
      
    server_addr.sin_family = AF_INET;         //host byte order  
    server_addr.sin_port = htons(MYPORT);     //short, network byte order  
    server_addr.sin_addr.s_addr = INADDR_ANY; //automatically fill with my IP  
    memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));  
  
    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)//绑定套接字和服务器地址
    {  
        perror("bind");  
        exit(1);  
    }  
  
    if (listen(sock_fd, BACKLOG) == -1)//侦听监听套接字 
    {  
        perror("listen");  
        exit(1);  
    }  
  
    fd_set fdsr;  
    int maxsock;  
    struct timeval tv;  
  
    conn_amount = 0;  
    sin_size = sizeof(client_addr);  
    maxsock = sock_fd;  
    while (1) {  
        
        FD_ZERO(&fdsr);//清空文件描述符集合
        FD_SET(sock_fd, &fdsr);//将监听套接字加入到文件描述符集合中  
  
        tv.tv_sec = 30;//设置超时时间
        tv.tv_usec = 0;  
  
        for (i = 0; i < BACKLOG; i++) {
            if (fd_A[i] != 0) {  
                FD_SET(fd_A[i], &fdsr); //将新建的连接套接字加入到文件描述符集合 
            }  
        }  
  
        ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);//开始检查文件描述符集合中所有的文件描述符 
        if (ret < 0) {  
            perror("select" failed!\n);  
            break;  
        } else if (ret == 0) {  
            printf("timeout\n");  
            continue;  
        }  
  
        //开始匹配文件描述符集合中所有的文件描述符
        for (i = 0; i < conn_amount; i++) {  
            if (FD_ISSET(fd_A[i], &fdsr)) {  
                ret = recv(fd_A[i], buf, sizeof(buf), 0);     
                char str[] = "Good,very nice!\n";                 
                send(fd_A[i],str,sizeof(str) + 1, 0);  
                                  
                if (ret <= 0) {        // client close  
                    printf("client[%d] close\n", i);  
                    close(fd_A[i]);  
                    FD_CLR(fd_A[i], &fdsr);  
                    fd_A[i] = 0;  
                } else {        // receive data  
                    if (ret < BUF_SIZE)  
                        memset(&buf[ret], '\0', 1);  
                    printf("client[%d] send:%s\n", i, buf);  
                }  
            }  
        }  
  
        // 检查是否有新的连接请求
        if (FD_ISSET(sock_fd, &fdsr)) {  
            new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);  
            if (new_fd <= 0) {  
                perror("accept");  
                continue;  
            }  
  
           //将新的连接套接字加入到fd_A中
            if (conn_amount < BACKLOG) {  
                fd_A[conn_amount++] = new_fd;  
                printf("new connection client[%d] %s:%d\n", conn_amount,  
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));  
                if (new_fd > maxsock)  
                    maxsock = new_fd;  
            }  
            else {  
                printf("max connections arrive, exit\n");  
                send(new_fd, "bye", 4, 0);  
                close(new_fd);  
                break;  
            }  
        }  
        showclient();  
    }  
  
    // close other connections  
    for (i = 0; i < BACKLOG; i++) {  
        if (fd_A[i] != 0) {  
            close(fd_A[i]);  
        }  
    }  
  
    exit(0);  
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值