一、poll函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 请求的事件 */
short revents; /* 返回的事件 */
};
nfds:fds的个数
timeout:设置阻塞的时间(毫秒)
0为非阻塞
负数表示永久阻塞
事件类型
events:
POLLIN:有数据可读
••POLLPRI:有紧急数据需要读取•POLLOUT: 文件可写.....
二、poll的实现
头文件
#ifndef _NET_H_
#define _NET_H_
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>
typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5
#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)
void Argment(int argc, char *argv[]);
int CreateSocket(char *argv[]);
int DataHandle(int fd);
#endif
poll实现
#include "net.h"
#include <poll.h>
#define MAX_SOCK_FD 1024
int main(int argc, char *argv[])
{
int i, j, fd, newfd;
nfds_t nfds = 1;
struct pollfd fds[MAX_SOCK_FD] = {};//pollfd结构体数组,用于存储要被监视的文件描述符集合
Addr_in addr; //网络地址结构体变量addr
socklen_t addrlen = sizeof(Addr_in); //地址长度变量addrlen
/*检查参数,小于3个 直接退出进程*/
Argment(argc, argv); //Argment函数检查命令行参数的合法性
/*创建已设置监听模式的套接字*/
fd = CreateSocket(argv); //创建一个监听套接字
fds[0].fd = fd; //将监听套接字fd添加到被监视的文件描述符集合中
fds[0].events = POLLIN; //并设置其关注的事件为POLLIN(有数据可读)
while(1){
if( poll(fds, nfds, -1) < 0) //poll函数监视文件描述符集合
ErrExit("poll");
for(i = 0; i < nfds; i++){ //遍历所有被监视的文件描述符
/*接收客户端连接,并生成新的文件描述符*/
if(fds[i].fd == fd && fds[i].revents & POLLIN){ //如果当前文件描述符等于监听套接字,并且有数据可读,则接收新的客户端连接。
if( (newfd = accept(fd, (Addr *)&addr, &addrlen) ) < 0) //accept函数接收客户端连接
perror("accept");
fds[nfds].fd = newfd; //将新的客户端连接添加到被监视的文件描述符集合中
fds[nfds++].events = POLLIN; //并设置其关注的事件为POLLIN(有数据可读)
printf("[%s:%d][nfds=%lu] connection successful.\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), nfds);
}
/*处理客户端数据*/
if(i > 0 && fds[i].revents & POLLIN){ //如果当前文件描述符不是监听套接字,并且有数据可读,则处理客户端数据
if(DataHandle(fds[i].fd) <= 0){
if( getpeername(fds[i].fd, (Addr *)&addr, &addrlen) < 0)
perror("getpeername");
printf("[%s:%d][fd=%d] exited.\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), fds[i].fd);
close(fds[i].fd);
for(j=i; j<nfds-1; j++)
fds[j] = fds[j+1];
nfds--;
i--;
}
}
}
}
close(fd);
return 0;
}
封装函数
#include "net.h"
void Argment(int argc, char *argv[]){
if(argc < 3){
fprintf(stderr, "%s<addr><port>\n", argv[0]);
exit(0);
}
}
int CreateSocket(char *argv[]){
/*创建套接字*/
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0)
ErrExit("socket");
/*允许地址快速重用*/
int flag = 1;
if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag) ) )
perror("setsockopt");
/*设置通信结构体*/
Addr_in addr;
bzero(&addr, sizeof(addr) );
addr.sin_family = AF_INET;
addr.sin_port = htons( atoi(argv[2]) );
/*绑定通信结构体*/
if( bind(fd, (Addr *)&addr, sizeof(Addr_in) ) )
ErrExit("bind");
/*设置套接字为监听模式*/
if( listen(fd, BACKLOG) )
ErrExit("listen");
return fd;
}
int DataHandle(int fd){
char buf[BUFSIZ] = {};
Addr_in peeraddr;
socklen_t peerlen = sizeof(Addr_in);
if( getpeername(fd, (Addr *)&peeraddr, &peerlen) )
perror("getpeername");
int ret = recv(fd, buf, BUFSIZ, 0);
if(ret < 0)
perror("recv");
if(ret > 0){
printf("[%s:%d]data: %s\n",
inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);
}
return ret;
}