Chinaunix首页 | 论坛 | 博客
  • 博客访问: 563862
  • 博文数量: 104
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1559
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-21 00:58
个人简介

锻炼精神,首先要锻炼肉体

文章分类

全部博文(104)

文章存档

2018年(1)

2016年(1)

2015年(101)

2014年(1)

我的朋友

分类: C/C++

2015-04-26 23:11:57

poll 是一种高级的轮询的方法,通常用于服务器端处理多个客户端的请求的时候。
其作用于 select 很相似,但是较比 select 方法而言,效率更高,并且处理的连接个数不受内核的限制。
若是使用 select 来轮询客户端的连接,可以接受的连接个数与内核中
进程所限定的可使用的文件描述符号的个数相同。
但是使用 poll 方法并不会有这种限制。


本篇文章主要介绍 poll 的API 描述和与poll方法配套使用的结构体 struct pollfd 内部的构成。
并且通过编写一个服务器与客户端一对一通信的方法来作为服务器端使用
poll 函数的实例。

struct pollfd 
struct pollfd
{
  int fd ; // 用于检测的文件描述符
  short events ; // 设置所用的事件类型
  short  revents ; // 检测所用的事件类型
} ;

这里的事件类型有一系列相应的宏变量与之对应,在本程序中仅仅使用服务器端通过连接套接字
读取来自客户端上面的数据所使用的宏的定义

POLLRDNORM ----> poll read normal (data)
除了普通数据之外还有有限带外数据和高优先级数据,这里不做介绍

poll 方法API:
#include
int poll ( struct pollfd *fdarray , unsigned long nfds , int timeout );

参数:
fdarray    :
指向数组的指针,这个数组是由 struct pollfd 作为元素构成的
nfds        :
与 select 相同,对应的是所监控的最大文件描述符的个数,
使用的时候传入当前最大的文件描述符号+1 即可 
timeout   : 
在这里可以用来设定 poll 的工作模式 :
阻塞模式,  非阻塞模式, 介于阻塞非阻塞之间的在限定时间内阻塞模式
阻塞模式:
               将 timeout = INFTIME 传入即可,当代码执行到 poll 函数的所在行的时候,
               若是 fdarray 数组中的所有套接字描述符上面都没有发生变化,则代码不会
              向下执行,而是卡在 poll 所在行,直到 fdarray 中的套接字描述符有发生变化
               poll 方法才会返回发生变化的全部套接字的个数,并继续向下执行
               若出错,返回 -1 
非阻塞模式:
             将 timeout = 0  传入即可,当代码执行到 poll 函数所在行的时候, 
             若是 fdarray 数组中的所有套接字均没有变化,则不作停留,立即返回 0
             若是 fdarray 数组中存在套接字发生变化的,则立即返回发生变化的套接字的总数
              若是 poll 内部出错,立即返回 -1 
固定时间内阻塞模式:
               将 timeout 设置为非零的整数,当代名执行到 poll 函数所在行的时候,
                会等待 timeout 秒,在时间到达的时候,返回在这一段时间内发生变化的
                套接字总个数(没有就返回 0)
               若是在 timeout(sec) 这段时间内有错误发生的话,立即返回 -1 
 
返回值:
0         : 没有任何套接字上没有变化
n(n>0) : 有 n 个套接字上面有变化(来可读数据,有了可写的数据)
-1        : poll 执行过程中出错


运行环境: linux编译工具 : g++ , autotools {autoscan , aclocal ,autoconf , autoheader, automake }
/test/---|
            |---client/
                     |---client.cpp

                     |---configure.in
                     |---Makefile.am
                    |---build.sh
            |----server/
                     |---server.cpp
                     |---configure.in
                     |---Makefile.am
                     |---build.sh

Client 
1. 用于非首次编译的编译脚本 build.sh (chmod 755 build.sh
)

点击(此处)折叠或打开

  1. #!/bin/sh
  2. autoscan
  3. aclocal
  4. autoconf
  5. autoheader #after autoheader , create a Makefile.am then continue execute follow command
  6. automake --add-missing
  7. ./configure CXXFLAGS= CFLAGS=
  8. make

2. 将执行 autoscan 命令之后生成的 configure.scan 修改之后并更名为 configure.in 文件

点击(此处)折叠或打开

  1. # -*- Autoconf -*-
  2. # Process this file with autoconf to produce a configure script.
  3. AC_INIT(message_sender_client)
  4. AC_CONFIG_SRCDIR([client.cpp])
  5. AC_CONFIG_HEADERS([config.h])
  6. AM_INIT_AUTOMAKE(message_sender_client, 1.0)
  7. # Checks for programs.
  8. AC_PROG_CXX
  9. AC_PROG_CC
  10. #add by Aimer , cause method use realloc
  11. AC_FUNC_REALLOC
  12. # Checks for header files.
  13. AC_CHECK_HEADERS([arpa/inet.h netinet/in.h strings.h sys/socket.h unistd.h])
  14. # Checks for typedefs, structures, and compiler characteristics.
  15. AC_TYPE_SSIZE_T
  16. # Checks for library functions.
  17. AC_CHECK_FUNCS([bzero socket])
  18. AC_OUTPUT(Makefile)

3. 执行 autoheader 命令之后,需要手动编写的 Makefile.am 文件

点击(此处)折叠或打开

  1. AUTOMAKE_OPTIONS=foreign
  2. bin_PROGRAMS=message_sender_client
  3. #define short names for bson path and boost path
  4. BSON_PATH=/unixC/Bson/bson/src
  5. BOOST_PATH=/unixC/Boost/boost_1_58_0
  6. message_sender_client_SOURCES=\
  7. client.cpp \
  8. $(BSON_PATH)/bsonobj.cpp $(BSON_PATH)/util/json.cpp \
  9. $(BSON_PATH)/oid.cpp $(BSON_PATH)/lib/base64.cpp \
  10. $(BSON_PATH)/lib/md5.cpp $(BSON_PATH)/lib/nonce.cpp
  11. message_sender_client_CXXFLAGS=\
  12. -I$(BOOST_PATH) -I$(BSON_PATH) \
  13. -D_FILE_OFFSET_BITS=64 -ggdb -Wall -O0
  14. message_sender_client_LDADD=\
  15. -lpthread -lm -lboost_system -lboost_thread \
  16. -lboost_thread -lboost_program_options -lrt
  17. message_sender_client_LDFLAGS=\
  18. -fPIC -rdynamic -L$(BOOST_PATH)/stage/lib -pthread

4. client.cpp

点击(此处)折叠或打开

  1. #include <cstdio> // perror
  2. #include <cstring> // string
  3. #include <strings.h> // memset
  4. #include <iostream>
  5. #include <sys/types.h> // AF_INET, SOCK_STREAM
  6. #include <sys/socket.h> // socket , connect
  7. #include <arpa/inet.h> // inet_aton
  8. #include <netinet/in.h>
  9. #include <unistd.h>


  10. #define SERVER_PORT 1027
  11. #define SERVER_IP "10.0.2.15"
  12. #define MAXLINE 1024*2

  13. using namespace std ;


  14. int main ( int argc , char **argv )
  15. {
  16.    char buf [MAXLINE] ;
  17.    ssize_t n ; // message content length
  18.    struct sockaddr_in server_addr ;
  19.    int connfd ;
  20.    int ret ;
  21.    string msg ;
  22.     
  23.    connfd = socket (AF_INET , SOCK_STREAM , 0 ) ;
  24.    bzero (&server_addr , sizeof(struct sockaddr_in)) ;
  25.   
  26.    server_addr.sin_family = AF_INET ;
  27.    server_addr.sin_port = htons (SERVER_PORT) ;
  28.    inet_aton (SERVER_IP , &server_addr.sin_addr ) ;
  29.    
  30.    ret = connect ( connfd , (struct sockaddr*)&server_addr , sizeof(struct sockaddr_in) ) ;    
  31.   

  32.  if ( ret < 0 )
  33.   {
  34.     perror ("failed connect") ;
  35.     return -1;
  36.   }
  37.   
  38.   cout << "input message "<< endl ;
  39.   cin >> msg ;
  40.   
  41.   write (connfd , msg.c_str() , msg.size() ) ;
  42.     

  43.  return 0 ;
  44. }



Server
1. 用于非首次编译的编译脚本 build.sh (chmod 755 build.sh)

点击(此处)折叠或打开

  1. #!/bin/sh
  2. autoscan
  3. aclocal
  4. autoconf
  5. autoheader #after autoheader , create a Makefile.am then continue execute follow command
  6. automake --add-missing
  7. ./configure CXXFLAGS= CFLAGS=
  8. make

2. 将执行 autoscan 命令之后生成的 configure.scan 修改之后并更名为 configure.in 文件

点击(此处)折叠或打开

  1. # -*- Autoconf -*-
  2. # Process this file with autoconf to produce a configure script.
  3. AC_INIT(message_sender_server)
  4. AC_USE_SYSTEM_EXTENSIONS # we will use bson and boost
  5. AM_INIT_AUTOMAKE(message_sender_server,1.0)
  6. AC_CONFIG_SRCDIR([server.cpp])
  7. AC_CONFIG_HEADERS([config.h])
  8. # Checks for programs.
  9. AC_PROG_CXX
  10. AC_PROG_CC
  11. # Checks for libraries.
  12. # Checks for header files.
  13. AC_CHECK_HEADERS([limits.h netdb.h netinet/in.h strings.h sys/socket.h unistd.h])
  14. # Checks for typedefs, structures, and compiler characteristics.
  15. AC_TYPE_SSIZE_T
  16. # Checks for library functions.
  17. AC_CHECK_FUNCS([bzero socket])
  18. AC_OUTPUT(Makefile)

3. 执行 autoheader 命令之后,需要手动编写的 Makefile.am 文件

点击(此处)折叠或打开

  1. AUTOMAKE_OPTIONS=foreign
  2. bin_PROGRAMS=message_sender_server
  3. BSON_PATH=/unixC/Bson/bson/src
  4. BOOST_PATH=/unixC/Boost/boost_1_58_0
  5. message_sender_server_SOURCES=\
  6. server.cpp \
  7. $(BSON_PATH)/bsonobj.cpp $(BSON_PATH)/util/json.cpp \
  8. $(BSON_PATH)/oid.cpp $(BSON_PATH)/lib/base64.cpp \
  9. $(BSON_PATH)/lib/md5.cpp $(BSON_PATH)/lib/nonce.cpp
  10. message_sender_server_CXXFLAGS=\
  11. -I$(BOOST_PATH) -I$(BSON_PATH) -D_FILE_OFFSET_BITS=64 \
  12. -ggdb -Wall -O0
  13. message_sender_server_LDADD=\
  14. -lpthread -lm -lboost_system -lboost_thread \
  15. -lboost_thread -lboost_program_options -lrt
  16. message_sender_server_LDFLAGS=\
  17. -fPIC -rdynamic -L$(BOOST_PATH)/stage/lib -pthread

5. server.cpp

点击(此处)折叠或打开

  1. #include <cstdio>
  2. #include <limits.h>
  3. #include <sys/types.h>     // socket , bind , accept
  4. #include <sys/socket.h>     // socket , bind , socklen_t , accept
  5. #include <poll.h>     // poll, struct pollfd
  6. #include <netdb.h>     // socklen_t
  7. #include <netinet/in.h>     // struct sockaddr_in , sockaddr
  8. #include <errno.h>         // errno
  9. #include <unistd.h> // read, write
  10. #include <strings.h>        // bzero
  11. #include <iostream> // cout , endl , cin


  12. #define MAXLINE     1024*2
  13. #define OPEN_MAX     1024
  14. #define SERVER_PORT     1027
  15. #define INFTIM        1024

  16. using namespace std ;


  17. int main ( int argc , char ** argv )
  18. {
  19.     int i , maxi , listenfd , connfd , sockfd ;
  20.     int nready ;
  21.     ssize_t n ;
  22.     char buf[MAXLINE] ;
  23.     socklen_t client_len ;    
  24.     struct pollfd client[OPEN_MAX] ;
  25.     struct sockaddr_in client_addr , server_addr ;
  26.     
  27.     listenfd = socket ( AF_INET , SOCK_STREAM , 0 ) ;
  28.     bzero ( &server_addr , sizeof(struct sockaddr_in)) ;
  29.     
  30.     server_addr.sin_family = AF_INET ;
  31.     server_addr.sin_addr.s_addr = htonl (INADDR_ANY) ;
  32.     server_addr.sin_port = htons ( SERVER_PORT ) ;
  33.     
  34.     bind (listenfd , (struct sockaddr*)&server_addr ,
  35.             sizeof(struct sockaddr_in) ) ;
  36.     
  37.     listen ( listenfd , 10 ) ;
  38.     
  39.     client[0].fd = listenfd ;
  40.     client[0].events = POLLRDNORM ; // poll read normal
  41.     
  42.     for ( i = 1 ; i < OPEN_MAX ; i++ )
  43.     {
  44.      client[i].fd = -1;
  45.     }
  46.     
  47.     maxi = 0 ;
  48.     
  49.     for ( ; ; )
  50.     {
  51.         nready = poll (client , maxi+1 , INFTIM) ;
  52.     
  53.      if ( client[0].revents & POLLRDNORM )
  54.      {
  55.          // this means new client connection request come
  56.          client_len = sizeof( struct sockaddr_in ) ;
  57.          connfd = accept ( listenfd , (struct sockaddr*)&client_addr ,
  58.             &client_len ) ;
  59.         
  60.          // updates maxi, and check if i out of limition of OPEN_MAX
  61.          for ( i = 1 ; i < OPEN_MAX ; i++ )
  62.          {
  63.             if ( client[i].fd < 0 )
  64.             {
  65.              client[i].fd = connfd ;
  66.              break ;
  67.             }    
  68.          } // for
  69.         
  70.          if ( i == OPEN_MAX )
  71.          {
  72.             perror ("too many client requests ") ;
  73.             return -1 ;
  74.          }
  75.         
  76.          client[i].events = POLLRDNORM ;
  77.         
  78.                   if ( i > maxi )
  79.          maxi = i ;
  80.             
  81.          if ( --nready <= 0 )
  82.             continue ;
  83.      } // if
  84.         
  85.         for ( i = 1 ; i <= maxi ; i++ )
  86.         {
  87.          if ( ( sockfd = client[i].fd ) < 0 )
  88.             continue ; // continue the sub for cycle
  89.             
  90.          if ( client[i].revents & (POLLRDNORM | POLLERR) )
  91.          {
  92.                    if ( ( n = read(sockfd , buf , MAXLINE)) < 0 )
  93.              {
  94.                 // read n < 0 , it must some error happen
  95.                  // if client reset connection , release client[i].fd
  96.                 // else return error code
  97.              if ( errno == ECONNRESET )
  98.              {
  99.                     // this means client reset connection
  100.                     close (sockfd) ;
  101.                     client[i].fd = -1 ;
  102.                 }
  103.                 else
  104.                 {
  105.                     perror ("error when server read from client") ;
  106.                     return -1 ;
  107.                 }

  108.              }
  109.              else if ( n == 0 )
  110.              {
  111.                 // this means client close connection
  112.                 close (sockfd) ;
  113.                 client[i].fd = -1 ;
  114.              }
  115.              else
  116.              {
  117.                 buf[n] = '\0' ;
  118.                 // n > 0 , server read something from client
  119.          cout << "receive from client [" << buf <<"]" <<endl ;
  120.             }

  121.          } // if POLLRDNORM | POLLERROR
  122.     
  123.         }// for
  124.         
  125.     } // main cycle
  126.     
  127.     return 0 ;
  128. }
运行方式
需要两个终端, 首先在终端1 中编译server.cpp ,运行 ./build.sh 
然后,运行生成的可执行二进制文件 ./message_sender_server

在终端 2 中编译 client.cpp , 运行 ./build.sh
然后,运行生成的可执行二进制文件 ./message_sender_client

运行结果
client 运行终端中输入 "Aimer"
server 运行终端中显示 "Aimer"

end 




阅读(5896) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~