(网络编程笔记):高并发服务器模型 - poll

目录

多路IO-poll

使用poll模型开发服务端流程

代码实现

多路IO-poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • 函数说明: 跟select类似, 委托内核监控可读, 可写, 异常事件
  • 函数参数:
    • fds: 一个struct pollfd结构体数组的首地址
struct pollfd {
       int   fd;    //要监控的文件描述符,如果fd为-1, 表示内核不再监控
       short events; //输入参数, 表示告诉内核要监控的事件, 读事件, 写事件, 异常事件
       short revents;//输出参数, 表示内核告诉应用程序有哪些文件描述符有事件发生    
   };
  • events/revents:
    • POLLIN:可读事件
    • POLLOUT: 可写事件
    • POLLERR: 异常事件
  • nfds: 告诉内核监控的范围, 具体是: 数组下标的最大值+1 
  • timeout: 
    • =0: 不阻塞, 立刻返回
    • -1: 表示一直阻塞, 直到有事件发生
    • >0: 表示阻塞时长, 在时长范围内若有事件发生会立刻返回; 如果超过了时长也会立刻返回

使用poll模型开发服务端流程

{
1 创建socket, 得到监听文件描述符lfd----socket()
2 设置端口复用----setsockopt()
3 绑定----bind()
4 监听----listen()
5 struct pollfd client[1024];
  client[0].fd = lfd;
  client[0].events = POLLIN;
  
  int maxi = 0;
  for(i=1; i<1024; i++)
  {
  	client[i].fd = -1;
  }
  
  while(1)
  {
  	nready = poll(client, maxi+1, -1);
  	//异常情况
  	if(nready<0)
  	{
  		if(errno==EINTR)  // 被信号中断
  		{
  			continue;
  		}
  		break;
  	}
  	
  	//有客户端连接请求到来
  	if(client[0].revents==POLLIN)
  	{
  		//接受新的客户端连接
  		cfd = accept(lfd, NULL, NULL);
  		
  		//寻找在client数组中可用位置
  		for(i=0; i<1024; i++)
  		{
  			if(client[i].fd==-1)
  			{
  				client[i].fd = cfd;
  				client[i].events = POLLIN;
  				break;
  			}
  		}
  		
  		//客户端连接数达到最大值
  		if(i==1024)
  		{
  			close(cfd);
  			continue;
  		}
  		
  		//修改client数组下标最大值
  		if(maxi<i)
  		{
  			maxi = i;
  		}
  		
  		if(--nready==0)
  		{
  			continue;
  		}
  	}
  	
  	//下面是有客户端发送数据的情况
  	for(i=1; i<=maxi; i++)
  	{
  		sockfd = client[i].fd;
  		//如果client数组中fd为-1, 表示已经不再让你内核监控了, 已经close了
  		if(client[i].fd==-1)
  		{
  			continue;
  		}
  		
  		if(client[i].revents==POLLIN)
  		{
  			//read 数据
  			n = read(sockfd, buf, sizeof(buf));
  			if(n<=0)
  			{
  				close(sockfd);
  				client[i].fd = -1;
  				
  			}
  			else 
  			{
  				//发送数据给客户端
  				write(sockfd, buf, n);
  			} 	
  			
	  		if(--nready==0)
	  		{
	  			break;
	  		}	
  		}  		
  	}  	
  }
 
  close(lfd); 
} 

代码实现

//IO多路复用技术poll函数的使用 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#include "wrap.h"

int main()
{
	int i;
	int n;
	int lfd;
	int cfd;
	int ret;
	int nready;
	int maxfd;
	char buf[1024];
	socklen_t len;
	int sockfd;
	fd_set tmpfds, rdfds;
	struct sockaddr_in svraddr, cliaddr;
	
	//创建socket
	lfd = Socket(AF_INET, SOCK_STREAM, 0);

	//允许端口复用
	int opt = 1;
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));

	//绑定bind
	svraddr.sin_family = AF_INET;
	svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	svraddr.sin_port = htons(8888);
	ret = Bind(lfd, (struct sockaddr *)&svraddr, sizeof(struct sockaddr_in));

	//监听listen
	ret = Listen(lfd, 128);

	struct pollfd client[1024];
	for(i=0; i<1024; i++)
	{
		client[i].fd = -1;
	}		

	//将监听文件描述符委托给内核监控----监控读事件
	client[0].fd = lfd;
	client[0].events = POLLIN;

	maxfd = 0; //maxfd表示内核监控的范围

	while(1)
	{
		nready = poll(client, maxfd+1, -1);
		if(nready<0)
		{
			perror("poll error");
			exit(1);
		}
		
		//有客户端连接请求
		if(client[0].fd==lfd && (client[0].revents & POLLIN))
		{
			cfd = Accept(lfd, NULL, NULL);

			//寻找client数组中的可用位置
			for(i=1; i<1024; i++)
			{
				if(client[i].fd==-1)
				{
					client[i].fd = cfd;
					client[i].events = POLLIN;
					break;
				}
			}

			//若没有可用位置, 则关闭连接
			if(i==1024)
			{
				Close(cfd);
				continue;
			}

			if(maxfd<i)
			{
				maxfd = i;
			}
			
			if(--nready==0)
			{
				continue;
			}
		}

		//下面是有数据到来的情况
		for(i=1; i<=maxfd; i++)
		{
			//若fd为-1, 表示连接已经关闭或者没有连接
			if(client[i].fd==-1)	
			{
				continue;
			}
			
			sockfd = client[i].fd;
			memset(buf, 0x00, sizeof(buf));
			n = Read(sockfd, buf, sizeof(buf));
			if(n<=0)
			{
				printf("read error or client closed,n==[%d]\n", n);
				Close(sockfd);
				client[i].fd = -1; //fd为-1,表示不再让内核监控
			}
			else
			{
				printf("read over,n==[%d],buf==[%s]\n", n, buf);
				write(sockfd, buf, n);
			}

			if(--nready==0)
			{
				break;
			}
		}
	}

	Close(lfd);
	return 0;
}

注意事项

  • 当poll函数返回的时候, 结构体当中的fd和events没有发生变化, 究竟有没有事件发生由revents来判断, 所以poll是请求和返回分离
  • struct pollfd结构体中的fd成员若赋值为-1, 则poll不会监控
  • 相对于select, poll没有本质上的改变; 但是poll可以突破1024的限制
    • 在/proc/sys/fs/file-max查看一个进程可以打开的socket描述符上限.
    • 如果需要可以修改配置文件: /etc/security/limits.conf
    • 加入如下配置信息, 然后重启终端即可生效.
* soft nofile 1024
* hard nofile 100000
  • soft和hard分别表示ulimit命令可以修改的最小限制和最大限制
  • 【注】:参考黑马linux C++教程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值