特征
-
和select机制一样,实现了IO多路复用;
-
不需要根据操作创建不同集合,所有事件在同一个集合中实现,用events成员输入事件,revents成员返回事件;
-
事件集合中每一个元素所占空间量更大,需要存储文件描述符、输入事件和返回事件;
-
对于分散的文件描述符可以使用事件集合顺序存储,在监视之后,顺序遍历,提高访问的效率。
流程
1.创建事件集合
2.将需要监视的文件描述符fd和读写事件添加到集合中
3.使用poll函数来检测是否有资源准备就绪
4.遍历事件集合数组,找到事件和对应的文件描述符,进行IO操作
相关API
#include <poll.h>
/**
* [poll 函数]
* @param fds [pollfd结构体集合]
* @param nfds [数组个数]
* @param timeout [超时时间]
* @return [-1 表示poll调用失败,修改errno的值; 0 阻塞超时返回 >0 有资源准备就绪返回]
*/
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
实例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>
int main()
{
int listenfd;
int connfd;
int ret;
int optval;
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
socklen_t addrlen = sizeof(cltaddr);
int fd;
char buf[128];
/* 1. 创建TCP流式套接字文件 */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror("socket");
return -1;
}
/* 设置套接字允许IP地址和端口被重新设置 */
optval = 1;/* 允许IP地址和端口被重新设置 */
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if (ret == -1)
{
perror("setsockopt");
return -1;
}
/* 2. 设置服务器主机的IP地址和端口号 */
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8888);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(listenfd, (const struct sockaddr *)&srvaddr, sizeof(srvaddr));
if (ret == -1)
{
perror("bind");
return -1;
}
/* 3. 启动服务器监听客户端的连接请求 */
ret = listen(listenfd, 1024);
if (ret == -1)
{
perror("listen");
return -1;
}
printf("listenfd = %d server init success\n", listenfd);
/* 创建事件集合 */
struct pollfd fds[1024];
int nfds;
int i;
/* 清空事件集合 */
for (i = 0; i < sizeof(fds) / sizeof(fds[0]); i++)
{
fds[i].fd = -1;/* 文件描述符是>=0的,设置为-1表示不关心 */
}
/* 将需要关心读事件的文件描述符listenfd添加到读事件中 */
fds[0].fd = listenfd;
fds[0].events = POLLIN | POLLOUT;
nfds = 1;/* 集合中有效数组元素的个数 */
while(1)
{
/* 以阻塞模式检测集合中是否有资源准备就绪的文件描述符,有则返回,没有会一直阻塞 */
ret = poll(fds, nfds, -1);
if(ret == -1)
{
perror("poll");
return -1;
}
else if (ret == 0)
{
fprintf(stderr, "poll timeout ...\n");
continue;
}
/* 循环处理资源准就绪的文件描述符的IO操作 */
for (i = 0; i < nfds; i++)
{
/* 判断返回事件 */
if (fds[i].revents == POLLIN)
{
/* 读事件 */
fd = fds[i].fd;
printf("fd = %d\n", fd);
if (fd == listenfd) /* 监听套接字资源准备就绪:说明有新的客户端发起连接请求 */
{
/* 4. 服务器等待监听客户端的连接请求并建立连接 */
connfd = accept(listenfd, (struct sockaddr *)&cltaddr, &addrlen);
if (connfd == -1)
{
perror("accept");
return -1;
}
printf("connfd = %d client connect success\n", connfd);
/* 有新的客户端连接成功,将新的通信套接字文件描述符和与之对应的事件添加事件集合fds中 */
fds[nfds].fd = connfd;
fds[nfds].events = POLLIN;
nfds += 1;
}
else /* 通信套接字资源准备就绪:说明有已经连接的客户端发送数据请求 */
{
/* 接收客户端的数据请求 */
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, sizeof(buf));
if (ret == -1)
{
perror("read");
return -1;
}
else if (ret == 0)
{
/*说明对端(客户端)退出*/
/* 将退出的文件描述符从事件集合中删除 */
int j;
for (j = i; j < nfds - 1; j++)
{
fds[j] = fds[j + 1];
}
fds[j].fd = -1;
nfds -= 1;
close(fd);
}
else
{
/* 调用write数据的回写 */
ret = write(fd, buf, sizeof(buf));
if (ret == -1)
{
perror("write");
return -1;
}
}
}
}
else if(fds[i].revents == POLLOUT)
{
/* 写事件 */
}
else
{
/* 其它事件 */
}
}
}
close(listenfd);
}
网友评论