多路复用select

Posted 高傲的monkey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多路复用select相关的知识,希望对你有一定的参考价值。

多路复用I/O:一个执行体监视多个文件描述符对象的状态是否改变,一旦改变通知其他执行体来实现。

基本思想:

1、 先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或者多个已准备好进行I/O时函数才返回

2、 函数返回时告诉进程那个描述符已准备就绪,可以进行I/O操作。

Select(int n,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout);

注意两点:

1、 select函数返回之前会将集合中状态未改变的fd清除

2、 返回值为状态改变的文件描述符对象的个数。

3、 Select阻塞时内部会产生一个线程盯着描述符对象的状态改变。

参数:n:maxfd,所有监控的文件描述符中值最大的加1

Read_fds:是否可读的文件描述符集

Write_fds:是否可写的文件描述符集

Except_fds:出错的文件描述符集

Timeout:如果设置为NULL,则会一直阻塞,直到文件描述符的状态改变

Struct timeval

{

Long tv_sec;

Long tv_usec;

} ;

 

文件描述符的几个宏:

FD_ZERO(fd_set *fdset):清空文件描述符集

FD_SET(int fd,fd_set *fdset):fd加入到fd_set集中

FD_ISSET(int fd,fd_set*fdset):判断fd是否在fdset

FD_CLR(fd,fd_set *fdset);fdfdset集中清除

 

Select函数使用范例(功能:本例使用的是tcpselect监听标准输入是否准备好字符串可读然后发送和socketfd是否可读,然后接受,实现以简单的qq通信)

客户端:
#include<stdio.h>
#include<pthread.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
int main()
{
    int socketfd,ret;
    fd_set fd;
    FD_ZERO(&fd);
    FD_SET(0,&fd);
        
    
    socketfd = socket(PF_INET,SOCK_STREAM,0);
    FD_SET(socketfd,&fd);
    ret = select(socketfd+1,&fd,NULL,NULL,NULL);
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = PF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.46");
    
    if(connect(socketfd,(struct sockaddr *)&saddr,sizeof(struct sockaddr))<0)

        perror("connect() error!\n");
    char buf[20],buf1[20];
    while(1)
    {
        bzero(buf,20);
        bzero(buf1,20);
        
    FD_SET(0,&fd);
    FD_SET(socketfd,&fd);
    ret = select(socketfd+1,&fd,NULL,NULL,NULL);
            if(FD_ISSET(socketfd,&fd))
            {
            if(recv(socketfd,buf1,sizeof(buf1),0)<0)
                perror("recv() error!\n");
                printf("%s",buf1);
                continue;
            }
            if(FD_ISSET(0,&fd))
            {
//            printf("please input:\n");
            fgets(buf,20,stdin);    
            if(send(socketfd,buf,strlen(buf),0)<0)
            perror("send() error!\n");
            continue;
            }    

        }
}    
服务器端:
#include<stdio.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<pthread.h>
#include<strings.h>
int main()
{
    int fds[12];
    int maxfd = -1;
    int socketfd = socket(PF_INET,SOCK_STREAM,0);
    if(socketfd<maxfd)
        maxfd = socketfd;
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = PF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("192.168.193.2");
    int newsocketfd;
    int n = 1;
    setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(int));
    if(bind(socketfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr_in))<0)
        perror("bind() error!\n");
    
    if(listen(socketfd,5)<0)
        perror("listen() error!\n");
    struct sockaddr_in caddr;
    int s = sizeof(struct sockaddr);
    newsocketfd=accept(socketfd,(struct sockaddr*)&caddr,&s);
    fd_set fd;
    FD_ZERO(&fd);
    int ret;
    char buf[20],buf1[20];
    int i;
    int rsize;
            printf("hello\n");
    while(1)
    {    
        bzero(buf,20);
        bzero(buf1,20);
//            printf("hello\n");
        FD_SET(0,&fd);    
//            printf("hello\n");
        FD_SET(newsocketfd,&fd);
//            printf("hello\n");
    select(newsocketfd+1,&fd,NULL,NULL,NULL);
        if(FD_ISSET(newsocketfd,&fd))
            {
//            printf("hello\n");
            if(recv(newsocketfd,buf1,sizeof(buf1),0)<0)
            perror("recv() error!\n");
            printf("%s",buf1);
//            printf("hello\n");
            continue;
            }
        
            if(FD_ISSET(0,&fd))
            {
            fgets(buf,20,stdin);    
            if(send(newsocketfd,buf,strlen(buf),0)<0)
            perror("send() error!\n");
            continue;
            }
    }
    close(socketfd);
    close(newsocketfd);

}

 

以上是关于多路复用select的主要内容,如果未能解决你的问题,请参考以下文章

I/O多路复用——epoll函数

我用select做多路复用踩到的坑

IO多路复用的三种机制Select,Poll,Epoll

多路转接(IO复用)接口介绍

python-IO多路复用,select模块

多路复用select