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)
-
#!/bin/sh
-
-
autoscan
-
aclocal
-
autoconf
-
autoheader #after autoheader , create a Makefile.am then continue execute follow command
-
automake --add-missing
-
./configure CXXFLAGS= CFLAGS=
-
make
-
2. 将执行 autoscan 命令之后生成的 configure.scan 修改之后并更名为 configure.in 文件
-
# -*- Autoconf -*-
-
# Process this file with autoconf to produce a configure script.
-
-
AC_INIT(message_sender_client)
-
AC_CONFIG_SRCDIR([client.cpp])
-
AC_CONFIG_HEADERS([config.h])
-
-
AM_INIT_AUTOMAKE(message_sender_client, 1.0)
-
-
-
# Checks for programs.
-
AC_PROG_CXX
-
AC_PROG_CC
-
-
#add by Aimer , cause method use realloc
-
AC_FUNC_REALLOC
-
-
# Checks for header files.
-
AC_CHECK_HEADERS([arpa/inet.h netinet/in.h strings.h sys/socket.h unistd.h])
-
-
# Checks for typedefs, structures, and compiler characteristics.
-
AC_TYPE_SSIZE_T
-
-
# Checks for library functions.
-
AC_CHECK_FUNCS([bzero socket])
-
-
AC_OUTPUT(Makefile)
-
3. 执行 autoheader 命令之后,需要手动编写的 Makefile.am 文件
-
AUTOMAKE_OPTIONS=foreign
-
bin_PROGRAMS=message_sender_client
-
-
#define short names for bson path and boost path
-
BSON_PATH=/unixC/Bson/bson/src
-
BOOST_PATH=/unixC/Boost/boost_1_58_0
-
-
message_sender_client_SOURCES=\
-
client.cpp \
-
$(BSON_PATH)/bsonobj.cpp $(BSON_PATH)/util/json.cpp \
-
$(BSON_PATH)/oid.cpp $(BSON_PATH)/lib/base64.cpp \
-
$(BSON_PATH)/lib/md5.cpp $(BSON_PATH)/lib/nonce.cpp
-
-
-
message_sender_client_CXXFLAGS=\
-
-I$(BOOST_PATH) -I$(BSON_PATH) \
-
-D_FILE_OFFSET_BITS=64 -ggdb -Wall -O0
-
-
message_sender_client_LDADD=\
-
-lpthread -lm -lboost_system -lboost_thread \
-
-lboost_thread -lboost_program_options -lrt
-
-
message_sender_client_LDFLAGS=\
-
-fPIC -rdynamic -L$(BOOST_PATH)/stage/lib -pthread
-
4. client.cpp
-
#include <cstdio> // perror
-
#include <cstring> // string
-
#include <strings.h> // memset
-
#include <iostream>
-
#include <sys/types.h> // AF_INET, SOCK_STREAM
-
#include <sys/socket.h> // socket , connect
-
#include <arpa/inet.h> // inet_aton
-
#include <netinet/in.h>
-
#include <unistd.h>
-
-
-
#define SERVER_PORT 1027
-
#define SERVER_IP "10.0.2.15"
-
#define MAXLINE 1024*2
-
-
using namespace std ;
-
-
-
int main ( int argc , char **argv )
-
{
-
char buf [MAXLINE] ;
-
ssize_t n ; // message content length
-
struct sockaddr_in server_addr ;
-
int connfd ;
-
int ret ;
-
string msg ;
-
-
connfd = socket (AF_INET , SOCK_STREAM , 0 ) ;
-
bzero (&server_addr , sizeof(struct sockaddr_in)) ;
-
-
server_addr.sin_family = AF_INET ;
-
server_addr.sin_port = htons (SERVER_PORT) ;
-
inet_aton (SERVER_IP , &server_addr.sin_addr ) ;
-
-
ret = connect ( connfd , (struct sockaddr*)&server_addr , sizeof(struct sockaddr_in) ) ;
-
-
-
if ( ret < 0 )
-
{
-
perror ("failed connect") ;
-
return -1;
-
}
-
-
cout << "input message "<< endl ;
-
cin >> msg ;
-
-
write (connfd , msg.c_str() , msg.size() ) ;
-
-
-
return 0 ;
-
}
Server
1. 用于非首次编译的编译脚本 build.sh (chmod 755 build.sh)
-
#!/bin/sh
-
-
autoscan
-
aclocal
-
autoconf
-
autoheader #after autoheader , create a Makefile.am then continue execute follow command
-
automake --add-missing
-
./configure CXXFLAGS= CFLAGS=
-
make
-
2. 将执行 autoscan 命令之后生成的 configure.scan 修改之后并更名为 configure.in 文件
-
# -*- Autoconf -*-
-
# Process this file with autoconf to produce a configure script.
-
-
-
AC_INIT(message_sender_server)
-
-
AC_USE_SYSTEM_EXTENSIONS # we will use bson and boost
-
-
AM_INIT_AUTOMAKE(message_sender_server,1.0)
-
-
AC_CONFIG_SRCDIR([server.cpp])
-
-
AC_CONFIG_HEADERS([config.h])
-
-
# Checks for programs.
-
AC_PROG_CXX
-
-
AC_PROG_CC
-
-
# Checks for libraries.
-
-
# Checks for header files.
-
AC_CHECK_HEADERS([limits.h netdb.h netinet/in.h strings.h sys/socket.h unistd.h])
-
-
# Checks for typedefs, structures, and compiler characteristics.
-
AC_TYPE_SSIZE_T
-
-
# Checks for library functions.
-
AC_CHECK_FUNCS([bzero socket])
-
-
AC_OUTPUT(Makefile)
-
3. 执行 autoheader 命令之后,需要手动编写的 Makefile.am 文件
-
AUTOMAKE_OPTIONS=foreign
-
bin_PROGRAMS=message_sender_server
-
-
BSON_PATH=/unixC/Bson/bson/src
-
BOOST_PATH=/unixC/Boost/boost_1_58_0
-
-
message_sender_server_SOURCES=\
-
server.cpp \
-
$(BSON_PATH)/bsonobj.cpp $(BSON_PATH)/util/json.cpp \
-
$(BSON_PATH)/oid.cpp $(BSON_PATH)/lib/base64.cpp \
-
$(BSON_PATH)/lib/md5.cpp $(BSON_PATH)/lib/nonce.cpp
-
-
message_sender_server_CXXFLAGS=\
-
-I$(BOOST_PATH) -I$(BSON_PATH) -D_FILE_OFFSET_BITS=64 \
-
-ggdb -Wall -O0
-
-
message_sender_server_LDADD=\
-
-lpthread -lm -lboost_system -lboost_thread \
-
-lboost_thread -lboost_program_options -lrt
-
-
message_sender_server_LDFLAGS=\
-
-fPIC -rdynamic -L$(BOOST_PATH)/stage/lib -pthread
-
5. server.cpp
-
#include <cstdio>
-
#include <limits.h>
-
#include <sys/types.h> // socket , bind , accept
-
#include <sys/socket.h> // socket , bind , socklen_t , accept
-
#include <poll.h> // poll, struct pollfd
-
#include <netdb.h> // socklen_t
-
#include <netinet/in.h> // struct sockaddr_in , sockaddr
-
#include <errno.h> // errno
-
#include <unistd.h> // read, write
-
#include <strings.h> // bzero
-
#include <iostream> // cout , endl , cin
-
-
-
#define MAXLINE 1024*2
-
#define OPEN_MAX 1024
-
#define SERVER_PORT 1027
-
#define INFTIM 1024
-
-
using namespace std ;
-
-
-
int main ( int argc , char ** argv )
-
{
-
int i , maxi , listenfd , connfd , sockfd ;
-
int nready ;
-
ssize_t n ;
-
char buf[MAXLINE] ;
-
socklen_t client_len ;
-
struct pollfd client[OPEN_MAX] ;
-
struct sockaddr_in client_addr , server_addr ;
-
-
listenfd = socket ( AF_INET , SOCK_STREAM , 0 ) ;
-
bzero ( &server_addr , sizeof(struct sockaddr_in)) ;
-
-
server_addr.sin_family = AF_INET ;
-
server_addr.sin_addr.s_addr = htonl (INADDR_ANY) ;
-
server_addr.sin_port = htons ( SERVER_PORT ) ;
-
-
bind (listenfd , (struct sockaddr*)&server_addr ,
-
sizeof(struct sockaddr_in) ) ;
-
-
listen ( listenfd , 10 ) ;
-
-
client[0].fd = listenfd ;
-
client[0].events = POLLRDNORM ; // poll read normal
-
-
for ( i = 1 ; i < OPEN_MAX ; i++ )
-
{
-
client[i].fd = -1;
-
}
-
-
maxi = 0 ;
-
-
for ( ; ; )
-
{
-
nready = poll (client , maxi+1 , INFTIM) ;
-
-
if ( client[0].revents & POLLRDNORM )
-
{
-
// this means new client connection request come
-
client_len = sizeof( struct sockaddr_in ) ;
-
connfd = accept ( listenfd , (struct sockaddr*)&client_addr ,
-
&client_len ) ;
-
-
// updates maxi, and check if i out of limition of OPEN_MAX
-
for ( i = 1 ; i < OPEN_MAX ; i++ )
-
{
-
if ( client[i].fd < 0 )
-
{
-
client[i].fd = connfd ;
-
break ;
-
}
-
} // for
-
-
if ( i == OPEN_MAX )
-
{
-
perror ("too many client requests ") ;
-
return -1 ;
-
}
-
-
client[i].events = POLLRDNORM ;
-
-
if ( i > maxi )
-
maxi = i ;
-
-
if ( --nready <= 0 )
-
continue ;
-
} // if
-
-
for ( i = 1 ; i <= maxi ; i++ )
-
{
-
if ( ( sockfd = client[i].fd ) < 0 )
-
continue ; // continue the sub for cycle
-
-
if ( client[i].revents & (POLLRDNORM | POLLERR) )
-
{
-
if ( ( n = read(sockfd , buf , MAXLINE)) < 0 )
-
{
-
// read n < 0 , it must some error happen
-
// if client reset connection , release client[i].fd
-
// else return error code
-
if ( errno == ECONNRESET )
-
{
-
// this means client reset connection
-
close (sockfd) ;
-
client[i].fd = -1 ;
-
}
-
else
-
{
-
perror ("error when server read from client") ;
-
return -1 ;
-
}
-
-
}
-
else if ( n == 0 )
-
{
-
// this means client close connection
-
close (sockfd) ;
-
client[i].fd = -1 ;
-
}
-
else
-
{
-
buf[n] = '\0' ;
-
// n > 0 , server read something from client
-
cout << "receive from client [" << buf <<"]" <<endl ;
-
}
-
-
} // if POLLRDNORM | POLLERROR
-
-
}// for
-
-
} // main cycle
-
-
return 0 ;
-
}
运行方式
需要两个终端, 首先在终端1 中编译server.cpp ,运行 ./build.sh
然后,运行生成的可执行二进制文件 ./message_sender_server
在终端 2 中编译 client.cpp , 运行 ./build.sh
然后,运行生成的可执行二进制文件 ./message_sender_client
运行结果
client 运行终端中输入 "Aimer"
server 运行终端中显示 "Aimer"
end
阅读(6016) | 评论(0) | 转发(0) |