I/O多路复用之select

select函数定义

#include <sys/select.h>

#include <sys/time.h>

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)


返回值:就绪描述符的数目,超时返回0,出错返回-1

参数介绍:

(1)第一个参数maxfdp1指定待测试的描述字个数,它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2...maxfdp1-1均将被测试,因为文件描述符是从0开始的。
(2)中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:
          void FD_ZERO(fd_set *fdset);           //清空集合
          void FD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中
          void FD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除
          int FD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写 
(3)timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。
         struct timeval
                   long tv_sec;   //seconds
                   long tv_usec;  //microseconds
       };
这个参数有三种可能:
(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。
(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。
(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0


select的缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024


select服务端程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h> 


#define LISTENQ      1024
#define SERVPORT     7885
#define MAXLINE      4096

int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr, cliaddr;
    int listenfd, connfd;
    fd_set allset, rset;
    int result;
    socklen_t client_len;

    ssize_t n;                          //read()读取的大小
    char buf[MAXLINE];

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd  == -1)
    {
        perror("create socket error");
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERVPORT);
    inet_pton(AF_INET, "INADDR_ANY", &servaddr.sin_addr);


    // 设置套接字选项避免地址使用错误 
    int on =1;
    if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  
    {  
        perror("setsockopt failed");  
        exit(EXIT_FAILURE);  
    }  
    
    if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    {
        perror("bind the socket");
        return -1;
    }

    if(listen(listenfd, LISTENQ) == -1)
    {
        perror("listen the socket");
        return -1;
    }

    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);

    while(1)
    {
        int fd;
        int nread;
        rset = allset;
        FD_ZERO(readfds);
        result = select(FD_SETSIZE, &rset, NULL, NULL, NULL);
        if(result < 1)
        {
            perror("server 5");
            exit(1);
        }

        //扫描所有文件描述符
        for(fd=0; fd<FD_SETSIZE; fd++)
        {
            if (FD_ISSET(fd, &rset))
            {
                if(fd == listenfd)//如果此描述符是监听描述符,则处理客户连接请求
                {
                    client_len = sizeof(cliaddr);
                    connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &client_len);
                    if(connfd == -1)
                    {
                        perror("accept error");
                        return -1;
                    }
                    FD_SET(connfd, &allset);
                    printf("add client on fd %d\n", connfd);
                }
                else //客户socket有数据请求时
                {
                    ioctl(fd, FIONREAD, &nread);//取得数据量交给nread  
                    if(nread == 0)
                    {
                        close(fd);
                        FD_CLR(fd, &allset);
                        printf("remove client on fd %d\n", fd);
                    }
                    else
                    {
                        memset(buf, 0, sizeof(buf));
                        n = read(fd, buf, MAXLINE);
                        printf("serving client on fd %d n=%d buf=%s\n", fd,n,buf);
                        write(fd, buf, n);
                    }

                }
            }
        }
    }
}

客户端测试程序:

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

#define MAXLINE      4096
#define SERVPORT     7885


int main(int argc, char *argv[]) 
{ 
    int client_sockfd; 
    int len; 
    struct sockaddr_in address;//服务器端网络地址结构体                                           
    int result; 
    char sendline[MAXLINE],recvline[MAXLINE];

    if(argc != 2)
    {
       printf("usage:tcpcli <IPaddress>\n");
       return 0;
    }


    client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立客户端socket                               
    address.sin_family = AF_INET; 
    inet_pton(AF_INET, argv[1], &address.sin_addr);         
    address.sin_port = htons(SERVPORT); 
    len = sizeof(address); 
    result = connect(client_sockfd, (struct sockaddr *)&address, len); 
    while(1)
    {
        if(result == -1) 
        { 
             perror("oops: client2"); 
             exit(1); 
        } 
        if(fgets(sendline, MAXLINE, stdin) == NULL)
        {
            return -1;
        }

        printf("sendline is %s\n", sendline);
        write(client_sockfd, sendline, strlen(sendline)); 

        //每次接收前清空recvline
        memset(recvline, 0, sizeof(recvline));
        read(client_sockfd, recvline, strlen(sendline)); 
        printf("char from server = %s\n", recvline); 
    }
    
    close(client_sockfd); 
   return 0;
}                      


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值