unix网络编程(三) 两种模式下的epoll服务端

1.水平触发和边沿触发

Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。

Level Triggered (LT) 水平触发只要有数据都会触发。

LT模式是默认的工作模式,在这种模式下epoll相当于一个效率较高的poll。使用ET模式需要在内核的事件表注册文件描述符的EPOLLET事件。

2.水平触发下的epoll服务器

编写epoll服务器的核心步骤:

  • 创建绑定监听IP和端口的套接字lfd
  • 创建内核事件表epfd
  • 注册lfd  到内核事件表epfd
  • 循环监听内核事件触发epoll_wait()
  • 触发事件的套接字连接分为监听套接字和连接套接字
  • 处理监听套接字(accept--->新连接注册到事件表)
  • 处理连接套接字(read读数据、处理数据)
#include
#include
#include
#include
#include
#include
#include "pub.h"
#define OPENMAX 1024

int main(int argc, char *argv[])
{
	if(argc<3)
	{
		printf("usage: ./%s ip_address port_num\n", basename(argv[0]));
		return -1;
	}
	char *ip = argv[1];
	int port = atoi(argv[2]);

	int lfd = -1, cfd = -1, epfd = -1, nready=-1, ret = -1;
	// 绑定、监听
	lfd = socketBind(ip, port);
	if(-1 == lfd)
		return -1;

	epfd = epoll_create(OPENMAX);
	if(epfd < 0)
	{
		perror("epoll_create");
		return -1;
	}

	// 向epoll事件表注册lfd和lfd的事件(上树)
	struct epoll_event tmp_ev, client_ev[OPENMAX];
	memset(client_ev, 0, sizeof(client_ev));

	tmp_ev.events = EPOLLIN;
	tmp_ev.data.fd = lfd;
	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tmp_ev);
	if(-1 == ret)
	{
		perror("epoll_ctl");
		return -1;
	}
	
	char buf[1024] = "";
	// 循环处理epoll事件
	while(1)
	{
		nready = epoll_wait(epfd, client_ev, OPENMAX, -1);
		if(-1 == nready)
		{
			perror("epoll_wait");
			break;
		}
		for(int i=0; i

2.边缘触发下的epoll服务器

边缘触发与水平触发服务器的核心步骤基本相似,其不同在于注册新连接事件时需要注册边缘EPOLLET模式,此外读写数据需要采用循环的方式,因此文件描述符应该设置为非阻塞模式。

编写边缘epoll服务器的核心步骤:

  • 创建绑定监听IP和端口的套接字lfd
  • 创建内核事件表epfd
  • 注册lfd  到内核事件表epfd
  • 循环监听内核事件触发epoll_wait()
  • 触发事件的套接字连接分为监听套接字和连接套接字
  • 处理监听套接字(accept--->新连接注册到事件表(注册EPOLLET模式))
  • 处理连接套接字(循环read读数据(非阻塞)、处理数据)

 epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

#include
#include
#include
#include
#include
#include
#include "pub.h"
#include

#define OPENMAX 1024
// 边缘模式ET,需要将fd设为非阻塞读取,避免循环读数据时阻塞。
int setnonblocking(int fd)
{
	int old_flag = fcntl(fd, F_GETFL);
	int new_flag = old_flag | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_flag);
	return new_flag;
}
int main(int argc, char *argv[])
{
	if(argc<3)
	{
		printf("usage: ./%s ip_address port_num\n", basename(argv[0]));
		return -1;
	}
	char *ip = argv[1];
	int port = atoi(argv[2]);

	int lfd = -1, cfd = -1, epfd = -1, nready=-1, ret = -1;
	// 绑定、监听
	lfd = socketBind(ip, port);
	if(-1 == lfd)
		return -1;

	epfd = epoll_create(OPENMAX);
	if(epfd < 0)
	{
		perror("epoll_create");
		return -1;
	}

	// 向epoll事件表注册lfd和lfd的事件(上树)
	struct epoll_event tmp_ev, client_ev[OPENMAX];
	memset(client_ev, 0, sizeof(client_ev));

	tmp_ev.events = EPOLLIN;
	tmp_ev.data.fd = lfd;
	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tmp_ev);
	if(-1 == ret)
	{
		perror("epoll_ctl");
		return -1;
	}
	
	char buf[10] = "";
	// 循环处理epoll事件
	while(1)
	{
		nready = epoll_wait(epfd, client_ev, OPENMAX, -1);
		if(-1 == nready)
		{
			perror("epoll_wait");
			break;
		}
		for(int i=0; i

你可能感兴趣的:(unix网络编程,linux,后端)