Linux poll
poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。
1、函数原型 #include <poll.h>
int poll(struct pollfd fd[], nfds_t nfds, int timeout);
2、函数参数:
(1)fd:一个结构数组,struct pollfd结构如下:
struct pollfd
{
int fd; //文件描述符
short events; //请求的事件
short revents; //返回的事件
};
events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。如果fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。
poll函数的事件标志符值
常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
注意:后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。
(2)第二个参数nfds:要监视的描述符的数目。
(3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果 它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。 INFTIM:永远等待;0:立即返回,不阻塞进程;>0:等待指定毫秒数。
#ifndef _POLL_H_
#define _POLL_H_
#include "wrap.h"
#include "client_list.h"
#include "server_queue.h"
#define SERVER_PORT 6780
#define MAXLINE 100
#define OPEN_MAX 65535
#define TCP_FRAME_SIZE 1200
typedef struct
{
int sockfd; // server socket
int port; // server port
struct sockaddr_in addr; // server addr
int maxi; // poll max
struct pollfd event_list[OPEN_MAX]; // poll all event
server_queue_t send_queue; // server send data queue to client
server_queue_t recv_queue; // server recv data queue from client
pthread_t send_thread;
pthread_t recv_thread;
client_t *client; // client list -- save all client info
} server_t;
/* recv and send queue frame */
typedef struct
{
int sockfd; // client socket
uint16_t length;
char data[TCP_FRAME_SIZE];
} __packed tcp_frame_t;
//==========================================================
server_t *SocketInit(void);
#endif /* _POLL_H_ */
#include "poll.h"
#include "debug.h"
static server_t *socket_init(void)
{
int opt = 1, i;
server_t *current;
current = (server_t *)malloc(sizeof(server_t));
current->port = SERVER_PORT;
current->sockfd = Socket(AF_INET, SOCK_STREAM, 0);
// SOL_SOCKET: port can same, ip not
Setsockopt(current->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
current->addr.sin_family = AF_INET;
current->addr.sin_port = htons(current->port);
current->addr.sin_addr.s_addr = INADDR_ANY;
Bind(current->sockfd, (struct sockaddr *)¤t->addr, sizeof(current->addr));
Listen(current->sockfd, MAXLINE);
current->event_list[0].fd = current->sockfd;
current->event_list[0].events = POLLIN;
for(i = 1; i < OPEN_MAX; ++i)
{
current->event_list[i].fd = -1;
}
current->maxi = current->sockfd;
ServerQueueInit(¤t->send_queue, TCP_FRAME_SIZE);
ServerQueueInit(¤t->recv_queue, TCP_FRAME_SIZE);
return current;
}
static void socket_accept(server_t *arg)
{
server_t *current = arg;
struct sockaddr_in addr;
int len = sizeof(struct sockaddr_in), i;
int new_fd = Accept(current->sockfd, (struct sockaddr *)&addr, &len);
debug("new connection client_fd ( %d ) %s: %d\n", new_fd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
for(i = 0; i < OPEN_MAX; ++i)
{
if(current->event_list[i].fd < 0)
{
// add new_fd to event_list
current->event_list[i].fd = new_fd;
current->event_list[i].events = POLLIN;
break;
}
}
if(OPEN_MAX == i)
{
printf("too many connects\n");
Close(new_fd);
return;
}
if(i > current->maxi)
{
current->maxi = i;
}
/* add client node */
client_t *node = (client_t *)malloc(sizeof(client_t));
node->sockfd = new_fd;
memcpy(&node->addr, &addr, sizeof(struct sockaddr_in));
ClientAdd(node);
}
static void socket_recv(server_t *arg, int ret)
{
server_t *current = arg;
int i, sockfd, length = 0;
tcp_frame_t write;
for(i = 1; i <= current->maxi; ++i)
{
if((sockfd = current->event_list[i].fd) < 0)
{
continue;
}
if(current->event_list[i].revents & (POLLIN | POLLERR))
{
length = recv(sockfd, write.data, TCP_FRAME_SIZE, 0);
if(0 == length)
{
/* delete client node, close connect socket */
debug("client[%d] close\n", sockfd);
ClientDel(sockfd);
Close(sockfd);
continue;
}
else if(length > 0)
{
write.sockfd = sockfd;
write.length = length;
server_debug(write.data, write.length);
// add data to recv_queue, pop in other,
if(ServerQueueWrite(¤t->recv_queue, (uint8_t *)&write, sizeof(tcp_frame_t)) == 0)
{
debug("push failure...queue full...\n");
}
}
if(--ret <= 0)
{
break;
}
}
}
}
static void *server_recv_thread(void *arg)
{
server_t *current = (server_t *)arg;
while(1)
{
int timeout = 3000;
int ret = Poll(current->event_list, current->maxi, timeout);
if(current->event_list[0].revents & POLLIN)
{
socket_accept(current); // a new connect come
}
if(--ret < 0)
{
continue;
}
socket_recv(current, ret); // a exsit connect send data to us
}
Close(current->sockfd);
return NULL;
}
static void *server_send_thread(void *arg)
{
server_t *current = (server_t *)arg;
tcp_frame_t *read = NULL;
while(1)
{
//read = (tcp_frame_t*)ServerQueueRead(¤t->send_queue, sizeof(tcp_frame_t));
if(read != NULL)
{
//server_debug(read->data, read->length);
}
usleep(100);
}
return NULL;
}
server_t * SocketInit(void)
{
server_t *current = socket_init();
debug("create thread...\r\n");
pthread_create(¤t->send_thread, NULL, server_send_thread, current);
pthread_create(¤tt->recv_thread, NULL, server_recv_thread, current);
return current;
}