poll
在指定的时间内,轮询一定数量的文件描述符(该文件描述符存放在一个数组)
#include <poll.h>
int po1l(struct pollfd* fds, nfds_t nfds, int timeout);
// ---------------------------
- fds 参数是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。
- nfds参数指定被监听事件集合fds的大小。其类型nfds_t的定义如下: typedef unsigned 1ong int nfds_t
- timeout 参数指定poll的超时值,单位是毫秒。当timeout为-1时,poll调用将永远阻塞,直到某个事件发生:当timecout为0时,poll调用将立即返回。
// pollfd 结构体的定义如下:
struct pollfd{
int fd; /* 文件描述符 */
short events; /* 注册的事件 */
short revents; /*实际发生的事件,由内核填充*/
};
// --------------------------
- fd成员指定文件描述符:
- events 成员告诉poll监听fd上的哪些事件,它是一系列事件的按位或;
- revents 成员则由内核修改,以通知应用程序fd上实际发生了哪些事件(设置和select的区别);
poll系统调用的返回值的含义与select相同。
代码案例
服务端功能 : 将接收的客户端的数据逆转发送给所有连接的客户端
int main(){
// 1. 创建服务端socket
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if(server_socket == -1){
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 设置端口复用
int opt = 1;
if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == -1){
perror("socket opt failed");
exit(EXIT_FAILURE);
}
// 3. 设置服务器端口号和ip
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
// 4. 绑定ip和端口号
if(bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
perror("binding failed");
exit(EXIT_FAILURE);
}
// 5. 监听
if(listen(server_socket, 5) == -1){
perror("listening failed");
exit(EXIT_FAILURE);
}
// 6. 设置pollfd事件集(是一个数组,数组中的每个元素是pollfd结构体)
std::vector<pollfd> pollFds(1);
pollFds[0].fd = server_socket;
pollFds[0].events = POLLIN; // 可读事件
std::vector<int> clientSockets; // 存放所有客户端的socket
while(true){
int result = poll(pollFds.data(), pollFds.size(), -1);
if(result == -1){
perror("poll failed");
exit(EXIT_FAILURE);
}
for(const auto &pollFd : pollFds){
// 如果该事件是可读事件
if(pollFd.revents & POLLIN){
// 事件可读且是服务端socket时
if(pollFd.fd == server_socket){
// 创建新的客户端连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
if (client_socket == -1) {
perror("accept failed");
exit(EXIT_FAILURE);
}
std::cout<<"accept new connection id is : ["<<client_socket<<"]"<<std::endl;
// 给该连接的客户端设置事件,并放入pollfds中
pollfd newClient;
newClient.fd = client_socket;
newClient.events = POLLIN;
pollFds.push_back(newClient);
// 存放新连接的客户端socket
clientSockets.push_back(client_socket);
}
// 如果时客户端socket(文件fd),且时可读事件时
else{
// 读取客户端数据
char buffer[2048];
ssize_t byteRead = recv(pollFd.fd, buffer, sizeof(buffer)-1, 0);
if(byteRead <= 0){
// 客户端连接出错
std::cout << "Client disconnected" << std::endl;
close(pollFd.fd);
pollFds.erase(std::remove_if(
pollFds.begin(), pollFds.end(), [pollFd](const pollfd& fd){
return fd.fd == pollFd.fd;
}
),
pollFds.end()
);
clientSockets.erase(std::remove(
clientSockets.begin(), clientSockets.end(), pollFd.fd),
clientSockets.end()
);
}
else{
// 接收数据
buffer[byteRead] = '\0';
std::cout<<"receive from ["<<pollFd.fd<<"]"<<" data is : "<<buffer<<std::endl;
// 逆转数据,并返回(注意:poll的pollfd被内核修改revents,而events没有被修改)
std::string s(buffer);
std::reverse(s.begin(), s.end());
for(const auto &client : clientSockets){
std::cout<<"["<<server_socket<<"] send data to "<<"["<<client<<"]"<<std::endl;
send(client, s.data(), s.size(), 0);
}
}
}
}
}
}
close(server_socket);
return 0;
}