poll
程序思路同select函数用法
函数原型:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数1:传入传出参数,返回监听集响应状态
参数2:监听数量
参数3:超时判断,一般设为-1.
结构体定义
struct pollfd {
int fd; /* file descriptor /
short events; / requested events /
short revents; / returned events */
};
优化:
1.可以突破1024个文件描述符限制
2.监听、返回集合分离
3.poll自带监听描述符结构体数组,搜索范围可以缩小
修改文件描述符上限
cat /proc/sys/fs/file-max 查看文件描述符上限 (硬件层面)
sudo vi /etc/security/limits.conf文件描述符设置文件,打开,尾部加上下面语句
soft nofile(软限制)
hard nofile(硬限制)
服务器端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<poll.h>
#include<errno.h>
#include<ctype.h>
#include<string.h>
#include<strings.h>
#define MAXLINE 80
#define SERV_PORT 6666
#define OPEN_MAX 1024
int main(int argc, char *argv[] )
{
int i,j,maxi,listenfd,connfd,sockfd;
int nready; //接收poll返回值
ssize_t n;
char buf[MAXLINE],str[INET_ADDRSTRLEN];
socklen_t clien_len;
struct pollfd client[OPEN_MAX]; //保存监听文件描述符
struct sockaddr_in clie_addr,serv_addr;
listenfd=socket(AF_INET,SOCK_STREAM,0);
int opt=1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//端口复用
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(SERV_PORT);
bind(listenfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
listen(listenfd,128);
client[0].fd=listenfd;//要监听的第一个文件描述符存入,处理新客户端连接请求
client[0].events=POLLIN;//监听读事件
for(i=1;i<OPEN_MAX;i++)
client[i].fd=-1;
maxi=0;//数组中存入元素下标最大值,poll函数参数要求
while(1)
{
nready=poll(client,maxi+1,-1);//阻塞监听客户端请求
//client[i].revents会返回响应状态
if(client[0].revents& POLLIN)//新客户端连接请求
{
clien_len=sizeof(clie_addr);
connfd=accept(listenfd,(struct sockaddr *)&clie_addr,&clien_len);
printf("*****************************************************\n");
printf("new client connect: ip: %s ---port: %d\n",
inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,str,sizeof(str)),
ntohs(clie_addr.sin_port));
for(i=1;i<OPEN_MAX;i++)
{
if(client[i].fd<0)
{
client[i].fd=connfd;//找到数组中空闲位置,放入新客户端
break;
}
}
if(i==OPEN_MAX)
{
perror("too many clients");
exit(1);
}
client[i].events=POLLIN;//设置刚刚返回的connfd,监听读事件
if(i>maxi)
maxi=i;//更新maxi,确保最大
if(--nready<=0)//没有更多就绪事件,继续回到poll阻塞等待
continue;
}
for(i=1;i<=maxi;i++)
{
if((sockfd=client[i].fd)<0)
continue;
if(client[i].revents & POLLIN)//寻找可读相应的客户端
{
if((n=read(sockfd,buf,sizeof(buf)))<0)
{
if(errno==ECONNRESET)//收到RST标志位
{
printf("client[%d] aborted connection\n",i);
close(sockfd);
client[i].fd=-1;
}
else
{
perror("read error!");
exit(1);
}
}
else if(n==0)//客户端关闭连接
{
printf("client[%d] closed \n",i);
close(sockfd);
client[i].fd=-1;
}
else
{
for(j=0;j<n;j++)
buf[j]=toupper(buf[j]);
write(sockfd,buf,n);
}
if(--nready<=0)//没有多余就绪事件,返回poll阻塞等待
break;
}
}
}
return 0;
}
客户端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<string.h>
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
int main()
{
int cfd,sfd;
struct sockaddr_in serv_addr;
socklen_t serv_addr_len;
char buf[BUFSIZ],clien_ip[BUFSIZ];
int n;
char *find;
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERV_PORT);//转换为网络字节序
//serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY可以自行寻找ip
inet_pton(AF_INET,SERV_IP,&serv_addr.sin_addr.s_addr);
cfd=socket(AF_INET,SOCK_STREAM,0);//创建本地套接字
// bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//绑定,客户端隐式绑定
connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr) );
while(1)
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);//从输入端读一行数据存入缓存区中
find=strchr(buf,'\n');
if(find)
*find='\0';//去掉fgets中的换行符
write(cfd,buf,strlen(buf));
n=read(cfd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,n);
printf("\n");
}
close(cfd);
return 0;
}
测试结果
客户端1:
客户端2:
服务端: