c/c++linux后台开发学习笔记 2.2.2 reactor在网络组件中的应用

本文详细介绍了网络编程中的关键概念,包括服务端与客户端的连接建立,主动和被动的连接断开,以及消息的接收和发送。重点讨论了IO多路复用,特别是epoll机制在连接建立、断开、数据到达和发送完毕时的角色。此外,还提到了阻塞IO和非阻塞IO的区别,并探讨了reactor模式在处理并发服务请求中的应用。
摘要由CSDN通过智能技术生成

网络编程关注的问题

连接的建立

分为两种:服务端处理接收客户端的连接,服务端作为客户端连接第三方服务;

int clientfd = accept(listenfd, addr, sz);

int connectfd = socket(AF_INET, SOCK_STREAM, 0); 
connect(connectfd, (struct sockaddr *)&addr, sizeof(addr));

连接的断开

分为两种:主动断开和被动断开;

// 主动关闭 
close(fd); 
shutdown(fd, SHUT_RDWR); 
// 主动关闭本地读端,对端写段关闭 
shutdown(fd, SHUT_RD); 
// 主动关闭本地写端,对端读段关闭 
shutdown(fd, SHUT_WR); 

// 被动:读端关闭 
int n = read(fd, buf, sz); 
if (n == 0) { 
	close_read(fd); 
	// close(fd);
}

// 被动:写端关闭 
int n = write(fd, buf, sz); 
if (n == -1 && errno == EPIPE) { 
	close_write(fd); 
	// close(fd);
}

close_read 和 close_write是我们自己写的函数,一般直接close即可

消息的到达

从读缓冲区中读取数据;

int n = read(fd, buf, sz);
if (n < 0) { //n == -1
	if (errno == EINTR || errno == EWOULDBLOCK)
		break;
	close(fd);
} else if (n == 0) {
	close(fd);
} else {
	// 处理buf
}

消息发送完毕

往写缓冲区中写数据;

int n = write(fd, buf, sz);
if (n == -1) {
	if (error == EINTR || errno == EWOULDBLOCK) {
		return;
	}
}

注意read和write的不同,非阻塞IO时,read一次没写完会返回写入的字符数,而write如果写不完则不写入任何数据,如果能写完则全部写完。

网络IO指责

检测IO

io 函数本身可以检测 io 的状态;但是只能检测一个 fd 对应的状态;
io 多路复用可以同时检测多个io的状态;
区别是:io函数可以检测具体状态;
io 多路复用只能检测出可读、可写、错误、断开等笼统的事件;

操作 IO

只能使用 io 函数来进行操作;分为两种操作方式:阻塞 io 和非阻塞 io;

阻塞 IO 和 非阻塞 IO

  • 阻塞在网络线程;
  • 连接的 fd 阻塞属性决定了 io 函数是否阻塞;
  • 具体差异在:io 函数在数据未到达时是否立刻返回;
// 默认情况下,fd 是阻塞的,设置非阻塞的方法如下;
int flag = fcntl(fd, F_GETFL, 0); 
fcntl(fd, F_SETFL, flag | O_NONBLOCK);

在这里插入图片描述

IO 多路复用

在这里插入图片描述

epoll

结构与接口

红黑树 – 保存所有监听的fd和事件,已经一个指向用户空间的data(fd或ptr)
双向链表 – 保存监听到事件的fd,会被复制到epoll_wait提供的用户队列(数组)中

连接建立

//处理请求
struct epoll_event ev = {0};
ev.events |= EPOLLIN;
epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &ev);
...
clientfd = accept(...)
ev.events |= EPOLLIN;
epoll_ctl(efd, EPOLL_CTL_ADD, clientfd, &ev);

//处理非阻塞的链接
int connectfd = socket(AF_INET, SOCK_STREAM, 0);
connect(connectfd, (struct sockaddr *)&addr, sizeof(addr));
struct epoll_event ev;
ev.events |= EPOLLOUT;
epoll_ctl(efd, EPOLL_CTL_ADD, connectfd, &ev);
//当写事件触发时,连接建立成功
if (status == e_connecting && e->events & EPOLLOUT) {
	status == e_connected;
}

连接断开

if (e->events & EPOLLRDHUP) close_read(fd); //读端关闭
if (e-e->events & EPOLLHUP) close(fd);      //读写都关闭

数据到达

if (e->events & EPOLLIN) {
	int n = read(fd, buf, sz);
	if (n<0) {
		if(errno == EINTR)
			continue; //read again
		if (errno == EWOULDBLOCK)
			break; //read next time
		close(fd);
	} else if (n == 0) {
		close_read(fd);
	}
}

数据发送完毕

if (e->events & EPOLLOUT) {
	int n = write(fd, buf, dz);
	if (n == -1) {
		if(errno == EINTR)
			continue; //try again
		if (errno == EWOULDBLOCK) {
			struct epoll_event ev;
			ev.events = EPOLLOUT;
			epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
		} else {
			close(fd);
		}
	} else { // n > 0, success
		epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
	}
}

reactor应用

The reactor design pattern is an event handling pattern (事件处理模式)for handling
service requests delivered concurrently to a service handler by one or more inputs(处理
一个或多个并发传递到服务端的服务请求). The service handler then demultiplexes the
incoming requests and dispatches them synchronously (同步)to the associated request
handlers.

单reactor

redis
在这里插入图片描述
适合业务逻辑简单的场景

redis 6.0 IO多线程优化

//---------工作线程处理--------//
int n = read(fd, buf, sz);
cmd, args = decode(buf, sz);
//--------主线程处理--------------//
out = business_logic(cmd, args);
//--------工作线程处理------------//
reply = encode(out);
write(fd, reply, sizeof(reply);

多reactor

多线程,多进程
memcache,nginx
在这里插入图片描述
每个线程或者进程一个reactor
如果数据交互小,基本不相干,则要比单reactor有效很多

参考资料

[1] 零声教育c/c++linux后台开发 2.2.2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值