2.2.2 redis,memcached,nginx网络组件

本文详细介绍了网络编程中的关键问题,包括连接建立、断开、消息到达及发送,并深入探讨了网络IO的职责,如检测与操作IO、IO多路复用、reactor模型及其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、网络编程关注的问题

1.连接建立

分为两种:
1.服务端处理接收客户端的连接;
2.服务端作为客户端连接第三方服务;
int clientfd = accept(listenfd, addr, sz);
// 举例为非阻塞io,阻塞io成功直接返回0;
int connectfd = socket(AF_INET, SOCK_STREAM, 0);
int ret = connect(connectfd, (struct sockaddr*)&addr, sizeof(addr));
connect检测功能:检测建立连接的状态
connect操作功能:具体连接成功
connect第一次调用返回 ENIPROGRESS(正在建立)

accept主要对三次握手时的全连接队列操作
accept检测功能:检测全连接队列里是否有未处理的连接信息
accept操作功能:把该结点取出,生成客户端fd,客户端ip地址和端口(五元组)

listen监听:服务器接收客户端的连接
listen(fd,backlog):在没有调用accept把数据取出前,三次握手结束时最多能堆积backlog大小的数据

2.连接断开

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

主动断开
close:关闭两个端
shutdown:关闭一个端,例如调用shutdown关闭客户端的读端,那么服务端对应的写端就关闭了,对应write = -1 && errno = EPIPE状态

被动断开:
read = 0:服务端的读端关闭
write = -1 && errno = EPIPE :服务端的写端关闭
有的服务器需要支持半关闭状态,需要知道具体是读端还是写端关闭
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);
    // write()
    // close(fd);
}
// 被动:写端关闭
int n = write(fd, buf, sz);
if (n == -1 && errno == EPIPE) {
    close_write(fd);
    // close(fd);
}

3.消息到达

从读缓冲区中读取数据
read:
read = -1 && EWOULDBLOCK :说明读缓冲区没有数据
read = -1 && EINTR:说明 read 操作被取消
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
}

4.消息发送

往写缓冲区中写数据
write:
write= -1 && EWOULDBLOCK :说明读缓冲区没有数据
write= -1 && EINTR:说明 read 操作被取消
int n = write(fd, buf, dz);
if (n == -1) {
    if (errno == EINTR || errno == EWOULDBLOCK) {
        return;
   }
    close(fd);
}
IO函数只能检测一条连接的就绪状态,以及操作一条连接的IO数据

二、网络IO的职责

1.检测IO

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

IO多路复用的职责:操作检测多个IO的就绪状态,但是从不操作具体的IO


A.连接建立
	1.接收连接
	socket  -> bind & listen -> listenfd 监听读事件
	读事件发生后才会知道客户端与服务器产生连接了,
	产生一个结点告诉epoll,触发相应的epoll后续流程
	
	2.主动连接(connect)
	在第三次握手时,客户端向服务器发送ACK包的同时给Epoll 发送信号;
	触发写事件,说明连接成功了

B.连接断开
	EPOLLRDHUP:说明此时服务端的读端关闭了
	EPOLLHUP:说明服务端的读写端全都关闭了
	
C.消息到达
	客户端给服务器发送消息,如何监听(读缓冲区是否有数据)
	监听客户端的fd ,客户端发送数据(向读缓冲区插入数据时)会给epoll发送信号,
	触发读事件,说明读缓冲区有数据了,接下来就可以调用read,操作IO
	
D.消息发送
	一般监听客户端fd的写事件
	write buffer 如果满,Epoll 监听写事件,如果write buffer 存在剩余空间,可写状态时,
	就会给epoll发送一个信号,触发写事件,用户就可以调用write写数据了
注:只有主动连接和消息发送时检测的是写事件,其他都是读事件。

在这里插入图片描述
在这里插入图片描述

2.操作IO——阻塞IO与非阻塞IO

只能使用 io 函数来进行操作;分为两种操作方式:阻塞 io 和非阻塞 io;
1.阻塞在网络线程;
2.连接的 fd 阻塞属性决定了 io 函数是否阻塞;
3.具体差异在:io 函数在数据未到达时是否立刻返回;

在这里插入图片描述

// 默认情况下,fd 是阻塞的,设置非阻塞的方法如下;
int flag = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag | O_NONBLOCK);
int n = read(fd,buf,sz);
int n = write(fd,buf,sz);
在数据准备阶段
阻塞时,对于读缓冲区,如果为空,调用read阻塞就会等待填充数据;
阻塞时,对于写缓冲区,如果不能写入数据,调用write阻塞就会等待直到可以写入数据;
非阻塞时,不管缓冲区里有没有数据,会立即返回。

3.IO多路复用——EPOLL

int epoll_wait(int epfd, struct epoll_event*
events, int maxevents, int timeout);
OP操作:通过一个系统调用操作 epoll创建的rb_tree
op:
 EPOLL_CTL_ADD //添加
 EPOLL_CTL_MOD //修改
 EPOLL_CTL_DEL //删除

在这里插入图片描述

1.调用epoll_ctl 添加到 epoll 中的事件与网卡驱动程序建立回调关系;
相应事件触发时会调用回调函数(ep_poll_callback);
将触发的事件拷贝到 rdlist 双向链表(就绪队列)中;

2.调用 epoll_wait 将会把 rdlist 中就绪事件拷贝到用户态中(epoll_event 填充一个用户态数组用来接收就绪队列中的数据)

3. maxevents 为预期取出的事件
   int epoll_wait 函数返回实际取出的事件
   
4.  timeout 调节阻塞或非阻塞
    timeout = -1 为阻塞特性
	timeout  = 0 为非阻塞的特性
	timeout  = 1000,阻塞最多1s

4.reactor

1.io多路复用 :检测
2.非阻塞io :检测事件触发后,操作io
3.异步事件:read -> fd  > callback
4.reactor为什么搭配非阻塞io?
	a.多线程环境  可以将一个 listenfd 放到多个 epoll 去处理 epoll惊群,但只要一个accept调用成功
	b.边缘触发下  读事件触发后,read在一次事件循环中把read buffer 读空
	c.select bug:当某个socket接收缓冲区有新数据分节到达,然后select报告这个socket描述符可读,
	但随后,协议栈检查到这个新分解检验和错误,然后丢弃这个分节,这时候调用read则无数据可读,
	如果socket没有被设置nonblocking,此read将阻塞当前线程

5.redis

1.环境
kv、数据结构、内存数据库
命令处理是单线程的

2.redis为什么要使用单reactor?
命令处理是单线程的 ->  单reactor
操作具体命令事件复杂度比较低

3.redis怎么处理reactor?
	1.创建listenfd
	2. bind listen
	3. listenfd注册读事件
	4. 读事件触发的回调 accept
	5. clientfd 注册读事件
	6. 读事件触发,将会回调readQueryFromClient
4.redis针对reactor做了哪些优化
	1.read,decode 		记录日志
	2.encode,write 	获取排行榜记录

6.reactor的应用

单线程
单线程
多线程
多线程
多进程
多进程

7.memcached

…未完待续

8.nginx

…未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值