linux网络编程技巧,[c++, linux]网络编程之 poll 的使用方法

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=

make2. 将执行 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 -pthread4. client.cpp

点击(此处)折叠或打开

#include // perror

#include // string

#include // memset

#include

#include // AF_INET, SOCK_STREAM

#include // socket , connect

#include // inet_aton

#include

#include

#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 -pthread5. server.cpp

点击(此处)折叠或打开

#include

#include

#include     // socket , bind , accept

#include     // socket , bind , socklen_t , accept

#include     // poll, struct pollfd

#include     // socklen_t

#include     // struct sockaddr_in , sockaddr

#include         // errno

#include // read, write

#include         // bzero

#include // 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 <

}

} // 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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值