Poll系统调用
poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以 测试其是否有就绪者。
#include <poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);
struct pollfd
{
int fd; //文件描述符
short events; //注册的事件
short revents; //实际发生的事件,由内核填充
};
fds
参数是一个pollfd
类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。
其中fd
成员指定文件描述符;events
成员告诉poll
监听fd
上的哪些事件,它是一系列事件的按位或;revents
成员则由内核修改,以通知应用程序fd
上实际发生了哪些事件。- poll事件类型
nfds
参数指定了被监听事件集合fds的大小。typedef unsigned long int nfds_t;
timeout
则指定poll的超时值
poll编程实例
poll编程流程
/* * 1.创建socket-->bind-->listen * 2.创建一个数组存储socketfd和事件 并初始化数组 * 3.先将servicefd放入数组 * 4.循环:① 创建poll,然后处理连接 * ② 如果是新连接,放入数组并注册事件 * ③ 如果断开就从中删除 * ④ 都不是就收发数据 */
头文件tcp_socket.h
#include<iostream>
#include<assert.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#define BUFF_SIZE 128
using namespace std;
class Socket
{
public:
Socket()
{
sockfd_ = socket(PF_INET, SOCK_STREAM, 0);
assert(sockfd_ >= 0);
}
int Get_socket()
{
return sockfd_;
}
~Socket()
{
close(sockfd_);
}
protected:
int sockfd_;
};
class Socket_Ser :public Socket
{
public:
Socket_Ser(const char* ip, int port = 6000, int backlog = 5)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = inet_addr(ip);
int ret = bind(sockfd_, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(sockfd_, backlog);
assert(ret != -1);
}
int Accept()
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(sockfd_, (struct sockaddr*)&client, &len);
return connfd;
}
int Recv(int fd, char* buffer, int size)
{
int ret = recv(fd, buffer, size - 1, 0);
if (ret == -1 || (strncmp(buffer, "end", 3) == 0))
{
return -1;
}
}
void Send(int fd, const char* buffer, int size)
{
send(fd, buffer, size, 0);
}
void Close_client(int fd)
{
close(fd);
}
};
class Socket_Cli :public Socket
{
public:
Socket_Cli(const char* ip, int port = 6000)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = inet_addr(ip);
int ret = connect(sockfd_, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
}
int Send(char* buffer, int size)
{
send(sockfd_, buffer, size, 0);
}
};
头文件tcp_poll.h
#include"tcp_socket.h"
#include <poll.h>
#include <list>
#define PollSize 100
class Poll
{
public:
Poll(const char* ip, int port = 6000, int backlog = 5) :ser(ip, port, backlog)
{
for (int i = 0; i < PollSize; i++) //数组初始化
{
cli_arr[i].fd = -1;
cli_arr[i].events = 0;
}
Insert(ser.Get_socket(), POLLIN);
}
public:
void Deal()
{
UsingPoll();
Deal_connect();
}
private:
void Insert(int fd, short events)
{
int i = 0;
for (; i < PollSize; i++)
{
if (cli_arr[i].fd == -1) //找到一个未用过的数组
{
cli_arr[i].fd = fd;
cli_arr[i].events = events;
break;
}
}
if (i == PollSize) cout << "client array is full!" << endl;
}
void Delete(struct pollfd& PollFd)
{
ser.Close_client(PollFd.fd);
PollFd.fd = -1;
PollFd.events = 0;
}
void Deal_connect()
{
for (int i = 0; i < PollSize; i++)
{
if (cli_arr[i].fd == ser.Get_socket()) //服务器端
{
if (cli_arr[i].revents & POLLIN) //新的连接请求
{
int connfd = ser.Accept();
cout << "client " << connfd << "link" << endl;
Insert(connfd, POLLIN | POLLRDHUP); //注册
}
}
else //客户端发送数据
{
if (cli_arr[i].revents & POLLRDHUP) //客户端断开
{
cout << "client " << cli_arr[i].fd << "unlink" << endl;
Delete(cli_arr[i]);
}
else if (cli_arr[i].revents & POLLIN) //客户端发送数据
{
char buffer[128] = {
0 };
ser.Recv(cli_arr[i].fd, buffer, 128);
cout << "recv form " << cli_arr[i].fd << ":" << buffer << endl;
}
}
}
}
int UsingPoll()
{
int n = poll(cli_arr, PollSize, -1);
if (n <= 0)
{
cout << "poll error" << endl;
return -1;
}
}
private:
Socket_Ser ser;
struct pollfd cli_arr[PollSize];
};
主文件tcp_poll.cpp
#include "tcp_poll.h"
int main(int argc, char* argv[])
{
if (argc <= 1)
{
cout << "errno! please input again" << endl;
}
Poll mypoll(argv[1]);
while (1)
{
mypoll.Deal();
}
}
参考文献
[1]游双.Linux高性能服务器编程.机械工业出版社,2043.5.