select()

//int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
//参数1 描素符的总数 它比所有文件描述符集合中的文件描述符的最大值大1, readfds、writefds、exceptset:分别指向可读、可写和异常等事件对应的描述符集合。timeout:用于设置select函数的超时时间,即告诉内核select等待多长时间之后就放弃等待。timeout == NULL 表示等待无限长的时间

服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    fd_set rd;  //结构体
    struct timeval tv;  //结构体
    int err;


    FD_ZERO(&rd);  //初始化放到_fd_set 变量
    FD_SET(0,&rd);  //把标准输入 放入rd中

    tv.tv_sec = 5;
    tv.tv_usec = 0;
    err = select(1,&rd,NULL,NULL,&tv);

    if(err == 0) //超时
    {
        printf("select time out!\n");
    }
    else if(err == -1)  //失败
    {
        printf("fail to select!\n");
    }
    else  //成功
    {
        printf("data is available!\n");
    }
    char ch;
    while(ch=getchar()!='\n');
    return 0;
}


#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>


int main(int argc, char* argv[])
{
    if(argc <= 2)
    {
        printf("usage: ip address + port numbers\n");
        return -1;
    }
 struct timeval tv;
    const char* ip = argv[1];
    int port = atoi(argv[2]);
    tv.tv_sec = 5;
    tv.tv_usec = 0;
        printf("ip: %s\n",ip);
        printf("port: %d\n",port);

    int ret = 0;
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET,ip,&address.sin_addr);
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET,SOCK_STREAM,0);
    if(listenfd < 0)
    {
        printf("Fail to create listen socket!\n");
        return -1;
    }

    ret = bind(listenfd,(struct sockaddr*)&address,sizeof(address));
    if(ret == -1)
    {
        printf("Fail to bind socket!\n");
        return -1;
    }

    ret = listen(listenfd,5); //监听队列最大排队数设置为5
    if(ret == -1)
    {
        printf("Fail to listen socket!\n");
        return -1;
    }

    struct sockaddr_in client_address;  //记录进行连接的客户端的地址
    socklen_t client_addrlength = sizeof(client_address);
    int connfd = accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
    if(connfd < 0)
    {
        printf("Fail to accept!\n");
        close(listenfd);
    }

    char buff[1024]; //数据接收缓冲区
    fd_set read_fds;  //读文件操作符
    fd_set exception_fds; //异常文件操作符
    FD_ZERO(&read_fds);
    FD_ZERO(&exception_fds);

    while(1)
    {
        memset(buff,0,sizeof(buff));
        //每次调用select之前都要重新在read_fds和exception_fds中设置文件描述符connfd,因为事件发生以后,文件描述符集合将被内核修改
        FD_SET(connfd,&read_fds);
        FD_SET(connfd,&exception_fds);

        ret = select(connfd+1,&read_fds,NULL,&exception_fds,&tv);
        if(ret < 0)
        {
            printf("Fail to select!\n");
            return -1;
        }



        if(FD_ISSET(connfd, &read_fds))
        {
            ret = recv(connfd,buff,sizeof(buff)-1,0);
            if(ret <= 0)
            {
                break;
            }

            printf("get %d bytes of normal data: %s \n",ret,buff);

        }
        else if(FD_ISSET(connfd,&exception_fds)) //异常事件
        {
            ret = recv(connfd,buff,sizeof(buff)-1,MSG_OOB);
            if(ret <= 0)
            {
                break;
            }

            printf("get %d bytes of exception data: %s \n",ret,buff);
        }

    }

    close(connfd);
    close(listenfd);


    return 0;
}

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

1、单个进程可监视的fd数量被限制,即能监听端口的大小有限。一般来说这个数目和系统内存关系很大,具体数目可以cat/proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.

2、 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。

3、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    int result;
    fd_set readfds, testfds;
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(8888);
    server_len = sizeof(server_address);
    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
    listen(server_sockfd, 5); //监听队列最多容纳5个


    FD_ZERO(&readfds); //初始化文件描素符集合
    FD_SET(server_sockfd, &readfds);//将服务器端socket加入到集合中
    while(1)
    {
        char ch;
        int fd;  //用于循环
        int nread;
        testfds = readfds;//将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量
        printf("server waiting\n");

        /*无限期阻塞,并测试文件描述符变动  FD_SETSIZE==1024*/
        result = select(FD_SETSIZE, &testfds, (fd_set *)0,(fd_set *)0, (struct timeval *) 0); //FD_SETSIZE:系统默认的最大文件描述符
        if(result < 1)
        {
            perror("server5");
            exit(1);
        }

        /*扫描所有的文件描述符*/
        for(fd = 0; fd < FD_SETSIZE; fd++)
        {
            /*找到相关文件描述符*/
            if(FD_ISSET(fd,&testfds))
            {
              /*判断是否为服务器套接字,是则表示为客户请求连接。*/
                if(fd == server_sockfd)
                {
                    client_len = sizeof(client_address);
                    client_sockfd = accept(server_sockfd,
                    (struct sockaddr *)&client_address, &client_len);
                    FD_SET(client_sockfd, &readfds);//将客户端socket加入到集合中
                    printf("adding client on fd %d\n", client_sockfd);
                    printf("ip,port%ld\n",client_address.sin_port);
                }
                /*客户端socket中有数据请求时*/
                else
                {
                    ioctl(fd, FIONREAD, &nread);//取得数据量交给nread

                    /*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */
                    if(nread == 0) //
                    {
                        close(fd);
                        FD_CLR(fd, &readfds); //去掉关闭的fd
                        printf("removing client on fd %d\n", fd);
                    }
                    /*处理客户数据请求*/
                    else
                    {
                        read(fd, &ch, 1);
                        sleep(5);
                        printf("serving client on fd %d\n", fd);
                        ch++;
                        write(fd, &ch, 1);
                    }
                }
            }
        }
    }

    return 0;
}
*/
posted @ 2019-06-03 22:23  countryboy666  阅读(266)  评论(0编辑  收藏  举报