Server Develop (四) select实现非阻塞sever

Select server

  linux 的socket函数分为阻塞和非阻塞两种方式,比如accept函数,在阻塞模式下,它会一直等待有客户连接。而在非阻塞情况下,会立刻返回。我们一般都希望程序能够运行在非阻塞模式下。一种方法就是做一个死循环,不断去查询各个socket的状态,但是这样会浪费大量的cpu时间。解决这个问题的一个方法就是使用select函数。使用select函数可以以非阻塞的方式和多个socket通信。当有socket需要处理时,select函数立刻返回,期间并不会占用cpu时间。 

  select函数原型:

//Select 函数原型
 int select(nfds, readfds, writefds, exceptfds, timeout)
 //nfds: select监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要  监视各文件中的最大文件号加一
 //readfds:select监视的可读文件句柄集合
 //writefds:select监视的可写文件句柄集合。
 //exceptfds:select监视的异常文件句柄集合。
 //timeout:本次select的超时结束时间。

   当readfds或writefds中映象的文件可读或可写或超时,本次select() 就结束返回。程序员利用一组系统提供的宏在select()结束时便可判 断哪一文件可读或可写。对Socket编程特别有用的就是readfds.

  select相关函数:

FD_ZERO(fd_set *fdset)
//清空fdset与所有文件句柄的联系。
 
FD_SET(int fd, fd_set *fdset)
//建立文件句柄fd与fdset的联系。
 
FD_CLR(int fd, fd_set *fdset)
//清除文件句柄fd与fdset的联系。
 
FD_ISSET(int fd, fdset *fdset)
//检查fdset联系的文件句柄fd是否可读写,>0表示可读写。
 

  具体步骤: 

//大致如下:……
 
int sockfd;
fd_set fdR;
int sock_fd[N];//save client_sockfd, size is N

struct timeval timeout;
 
while(1){
    FD_ZERO(&fdR);
    FD_SET(sockfd, &fdR);

    switch (select(sockfd + 1,&fdR, NULL, &timeout)) {
      case -1:error;
      case 0:timeout;
      default:
      if (FD_ISSET(sockfd, &fdR)) {
                now  read or recv something;
      }
     }  
/* if sock_serv is in &fdR, now accept() */
  if( FD_ISSET(sock_serv,&fdR) ){
      sock_fd[..] = now accept();
    }
}

  具体代码:

void select_server::starListen()
{
    int flag = listen(sock_serv, 5);
    if(flag < 0){
        perror("listen error!\n");
        close(sock_serv);
        return;
    }
    printf("listen!\n");
    fd_set client_fdset;    //监控文件描述符集合
    int maxsock;            //监控文件描述符中最大的文件号
    struct timeval tv;        //超时返回时间
    int client_sockfd[5];   //存放活动的sockfd
    bzero((void*)client_sockfd,sizeof(client_sockfd));
    int conn_amount = 0;    //用来记录描述符数量
    maxsock = sock_serv;

    char buffer[1024];

    while(1){

        //初始化文件描述符号到集合
        FD_ZERO(&client_fdset);
        //加入服务器描述符
        FD_SET(sock_serv,&client_fdset);

        //设置超时时间
        tv.tv_sec = 30; //30秒
        tv.tv_usec = 0;

        //把活动的句柄加入到文件描述符中
        for(int i = 0; i < 5; ++i){//程序中Listen中参数设为5,故i必须小于5
            if(client_sockfd[i] != 0){
                FD_SET(client_sockfd[i], &client_fdset);
            }
        }
        //printf("put sockfd in fdset!\n");
        //select函数
        flag = select(maxsock+1, &client_fdset, NULL, NULL, &tv);
        if(flag < 0){
            perror("select error!\n");
            break;
        }
        else if(flag == 0){
            printf("timeout!\n");
            continue;
        }

        //轮询各个文件描述符
        for(int i = 0; i < conn_amount; ++i){
            //FD_ISSET检查client_sockfd是否可读写,>0可读写
            if(FD_ISSET(client_sockfd[i], &client_fdset)){
                printf("start recv from client[%d]:\n",i);
                flag = recv(client_sockfd[i], buffer, 1024, 0);
                if(flag <= 0){
                    printf("client[%d] close\n", i);
                    close(client_sockfd[i]);
                    FD_CLR(client_sockfd[i], &client_fdset);
                    client_sockfd[i] = 0;
                }
                else{
                    printf("recv from client[%d] :%s\n", i, buffer);
                }
            }
        }

        //检查是否有新的连接,如果收,接收连接,加入到client_sockfd中
        if(FD_ISSET(sock_serv, &client_fdset)){
            //接受连接
            struct sockaddr_in client_addr;
            size_t size = sizeof(struct sockaddr_in);
            int sock_client = accept(sock_serv, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));
            if(sock_client < 0){
                perror("accept error!\n");
                continue;
            }
            //把连接加入到文件描述符集合中
            if(conn_amount < 5){
                client_sockfd[conn_amount++] = sock_client;

                bzero(buffer,1024);
                strcpy(buffer, "this is server! welcome!\n");
                send(sock_client, buffer, 1024, 0);

                printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

                bzero(buffer,sizeof(buffer));
                flag = recv(sock_client, buffer, 1024, 0);
                if(flag < 0){
                    perror("recv error!\n");
                    close(sock_serv);
                    return;
                }
                printf("recv : %s\n",buffer);

                if(sock_client > maxsock){
                    maxsock = sock_client;
                }
                else{
                    printf("max connections!!!quit!!\n");
                    break;
                }
            }
        }
    }

    for(int i = 0; i < 5; ++i){
        if(client_sockfd[i] != 0){
            close(client_sockfd[i]);
        }
    }
}

  源码:这里


本文 由 cococo点点 创作,采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。欢迎转载,请注明出处:
转载自:cococo点点 http://www.cnblogs.com/coder2012

时间: 2024-08-02 02:45:40

Server Develop (四) select实现非阻塞sever的相关文章

【整理】Socket编程之非阻塞connect(一)

非阻塞 connect:       在 TCP socket 被设置为非阻塞的情况下调用 connect ,若没有立即返回成功,则会返回 -1 以及 errno =  EINPROGRESS 的 错误,其表示连接操作正在进行中,但是尚未完成,与此同时 TCP 三次握手操作会同时进行.在这之后,我们可以通过调用 select 来检查这个链接是否建立成功.  非阻塞 connect 的三种用途:  可以在 TCP 三次握手的同时做一些其它的处理.connect 操作需要一个往返时间才能完成,从几个

面向连接的socket数据处理过程以及非阻塞connect问题

对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后必须调用bind绑定到一个指定的地址,然后调用int listen(int sockfd, int backlog);进行监听.此时服务器socket允许客户端进行连接,backlog提示没被accept的客户连接请求队列的大小,系统决定实际的值,最大值定义为SOMAXCONN在头文件<sys/so

关于java阻塞socket和非阻塞socket的应用区别

问题描述 关于java阻塞socket和非阻塞socket的应用区别 最近在学习NIO,在学习非阻塞Socket的时候 很困惑,不知道他相对于阻塞的Socket的优势 在哪,希望大神指点一二,在线等. 解决方案 阻塞就是一直等待结果返回,非阻塞就是立即返回,等有了结果了以后,再回调,事件通知你 解决方案二: 传统的阻塞式,每个连接必须要开一个线程来处理,并且没处理完线程不能退出. 非阻塞式,由于基于反应器模式,用于事件多路分离和分派的体系结构模式,所以可以利用线程池来处理.事件来了就处理,处理完

Python 命令行非阻塞输入的小例子_python

 随手google咗一下,基本上都用select实现非阻塞监听,但问题是,监听的是用select之后是不能像getchar()那样,即时收到单个字符的输入,必须要等待回车.     经过努力不怠咁google... [好吧,还是google.没有google什么也做不了.]     最后系一大堆英文资料入面,拼凑出如下可用的代码,实现了单个字符非阻塞输入.     show code below. 复制代码 代码如下: #!/usr/bin/python# -*- coding: utf-8 -

Python中使用select模块实现非阻塞的IO_python

Socket的英文原义是"孔"或"插座".作为BSD UNIX的进程通信机制,取后一种意思.通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务.Socket正如其英文原意那样,像一个多孔插座.一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110

Python异步非阻塞IO多路复用Select/Poll/Epoll使用

来源:http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的. 下面记录下分别基于Select/Poll/Epoll的echo server实现. Python Select Server,可监控事件数量有限制: #!/usr/bin/python # -*- coding: utf-8 -*- import select import sock

SQL Server中SELECT会真的阻塞SELECT吗?

在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.TEST WHERE OBJECT_ID =1 这个查询语句,其申请.释放的锁资源的过程如下所示:     而且从最常见的锁模式的兼容性表,我们可以看到IS锁与S锁都是兼容的,也就是说SELECT查询是不会阻塞SELECT查询的. 现有的授权模式 请求的模式 IS S U IX SIX X 意向共享

Tornado Web Server 2.0发布 非阻塞IO的Web服务器软件

Tornado web server 是使用Python编写出来的一个极轻量级.高可伸缩性和非阻塞IO的http://www.aliyun.com/zixun/aggregation/17117.html">Web服务器软件,著名的 Friendfeed 网站就是使用它搭建的. Tornado 跟其他主流的Web服务器框架(主要是Python框架)不同是采用epoll非阻塞IO,响应快速,可处理数千并发连接,特别适用用于实时的Web服务. 要使用它,必须按照以下套件: 1)Python(建

关于网络IO中的同步、异步、阻塞、非阻塞

在高并发编程当中,我们经常会遇到一些异步.非阻塞等一些概念,一些常用的技术比如异步的httpclient.netty nio.nginx.node.js等,它们的原理大都跟异步.非阻塞有关.特别是在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会导致资源浪费和时间延迟.通过事件注册.异步函数,开发人员可以提高资源的利用率,性能也会改善.其nginx和node.js处理并发都是采用的事件驱动异步非阻塞模式.其中nginx中处理并发用的是epoll,poll,queue等方式,node.js使