poll函数接口
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
参数说明
- fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.
- nfds表示fds数组的长度.
- timeout表示poll函数的超时时间, 单位是毫秒(ms).
- events和revents的取值具体我们可以参考man手册
返回结果
- 返回值小于0, 表示出错;
- 返回值等于0, 表示poll函数等待超时;
- 返回值大于0, 表示poll由于监听的文件描述符就绪而返回.
poll的优点
- 不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现.
- pollfd结构包含了要监视的events和发生的revents,不再使用select“参数-值”传递的方式. 接口使用比select更方便
- poll并没有最大数量限制 (但是数量过大后性能也是会下降)
poll的缺点
- poll中监听的文件描述符数目增多时和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.
- 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.
- 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降.
poll版本的TCP服务器代码
完整代码请前往我的GitHub仓库—>>点我哇
#pragma once
#include"tcp_socket.hpp"
#include<poll.h>
#include<unistd.h>
#include<cstring>
#include<vector>
class Poll{
public:
Poll()
:fds_(nullptr)
,fds_size_(15)
,nfds_(0)
,timeout_(1000)
{
fds_=new pollfd[fds_size_];
}
bool Add(const TcpSocket& sock){
if(nfds_==fds_size_){//需要扩容
pollfd *new_fds=new pollfd[2*fds_size_];
memcpy(new_fds,fds_,fds_size_*sizeof(pollfd));
fds_size_*=2;
delete[] fds_;
fds_=new_fds;
}
fds_[nfds_].fd=sock.GetFd();
fds_[nfds_].events=POLLIN;
++nfds_;
return true;
}
bool Del(const TcpSocket& sock){
int del_fd=sock.GetFd();
for(size_t i=0;i<nfds_;++i){
if(fds_[i].fd==del_fd){
fds_[i]=fds_[--nfds_];
break;
}
}
return true;
}
bool Wait(vector<TcpSocket>&list,int timeout=1000){
timeout_=timeout;
int ret=poll(fds_,nfds_,timeout_);
if(ret<0){
perror("Poll Wait");
return false;
}else if(ret==0){
cout<<"poll wait timeout"<<endl;
return false;
}
for(nfds_t i=0;i<nfds_;++i){
if(fds_[i].revents==POLLIN){
TcpSocket sock(fds_[i].fd);
list.push_back(sock);
}
}
return true;
}
int GetNfds(){
return nfds_;
}
const pollfd* GetFds(){
return fds_;
}
private:
pollfd *fds_;
nfds_t fds_size_;
nfds_t nfds_;
int timeout_;
};
typedef void (*Handler)(const string& buf,string& ret);
class TcpPollServer{
public:
TcpPollServer()
{}
~TcpPollServer(){
_sock.Close();
}
bool Start(const string& ip,const uint16_t port,Handler handler){
if(_sock.Socket()==false)
return false;
if(_sock.Bind(ip,port)==false)
return false;
if(_sock.Listen()==false)
return false;
Poll poll;
poll.Add(_sock);
while(1){
vector<TcpSocket> list;
if(poll.Wait(list)==false)
continue;
for(size_t i=0;i<list.size();++i){
if(list[i].GetFd()==_sock.GetFd()){
TcpSocket NewSock;
string ClientIp;
uint16_t ClientPort;
if(_sock.Accept(&NewSock,ClientIp,ClientPort)==false)
continue;
printf("[%s:%d]客户端已连接!~\n",ClientIp.c_str(),ClientPort);
poll.Add(NewSock);
}
else{
string msg;
int n=list[i].Recv(msg);
if(n==0){
poll.Del(list[i]);
list[i].Close();
continue;
}else{
cout<<"客户端发送:"<<msg<<endl;
string ret;
handler(msg,ret);
list[i].Send(ret);
}
}
}
}
return true;
}
private:
TcpSocket _sock;
};