Unix/Linux编程:网络编程封装总结
发布日期:2022-03-16 03:25:39 浏览次数:34 分类:技术文章

本文共 3157 字,大约阅读时间需要 10 分钟。

网络编程原则

明确:网络IO的职责

在这里插入图片描述

也就是说,封装是可以从两个层面来封装,一个是从操作IO的层面,比如各种接收、发送数据格式,监听数据流等,一个是从检测IO层面,也叫做事件引擎,专门用来检测IO事件。

比如redis的封装层面如下:

在这里插入图片描述
ps,是在【IO操作层】解决的:

  • 所谓粘包,就是多个包可能合并成一个包发送,需要分开了
  • 解决的关键在于想办法从收到的数据报中将包与包之间的边界区分出来,主要有三种方法:
    • 只发送固定包长度的数据报
    • 以指定字符(串)为包的结束标记
    • 包头+包体格式:包头是固定大小的,而且包头中必须含有一个字段来说明接下来的包体有多大。

关注:网络编程需要解决的四个问题

在这里插入图片描述

连接的建立

作为服务端,主要扮演两种角色:

  • 接收他人请求的连接
  • 服务端作为客户端去连接他人

在这里插入图片描述

连接的断开

分为主动断开和被动断开

在这里插入图片描述

消息的接收

在这里插入图片描述

消息的发送

在这里插入图片描述

关于epoll

结构以及接口

struct eventpoll {
// ... struct rb_root rbr; // 管理 epoll 监听的事件 struct list_head rdllist; // 保存着 epoll_wait 返回满⾜条件的事件 // ...};struct epitem {
// ... struct rb_node rbn; // 红⿊树节点 struct list_head rdllist; // 双向链表节点 struct epoll_filefd ffd; // 事件句柄信息 struct eventpoll *ep; // 指向所属的eventpoll对象 struct epoll_event event; // 注册的事件类型 // ...};struct epoll_event {
__uint32_t events; // epollin epollout epollel(边缘触发) epoll_data_t data; // 保存 关联数据};typedef union epoll_data {
void *ptr; int fd; uint32_t u32; uint64_t u64;}epoll_data_t;int epoll_create(int size);/**op: EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DELevent.events: EPOLLIN 注册读事件 EPOLLOUT 注册写事件 EPOLLET 注册边缘触发模式,默认是水平触发*/int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);/**events[i].events: EPOLLIN 触发读事件 EPOLLOUT 触发写事件 EPOLLERR 连接发生错误 EPOLLRDHUP 连接读端关闭 EPOLLHUP 连接双端关闭*/int epoll_wait(int epfd, struct epoll_event* events, int maxevents, inttimeout);

在这里插入图片描述

  • 调用epoll_create会创建一个epoll对象
  • 调用epoll_ctl添加到epoll中的事件都会与网卡驱动程序建立回调关系,相关事件触发时会调用回调函数 ( ep_poll_callback ),将触发的事件拷贝到rdlist双向链表中
  • 调用epoll_wait会将rdlist中就绪事件拷贝到用户态中

epoll编程

连接建立

(1)处理客户端的连接

// 1. 注册监听listenfd的读事件struct epoll_event ev;ev.events |= EPOLLIN;epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &ev);// 2. 当触发listenfd的读事件时,回调函数中需要调用accept接收新的连接int clientfd = accept(listenfd, addr, sz);struct epoll_event ev;ev.events |= EPOLLIN;epoll_ctl(efd, EPOLL_CTL_ADD, clientfd, &ev);

(2)去连接第三方服务

// 1. 创建 socket 建立连接int connectfd = socket(AF_INET, SOCK_STREAM, 0);connect(connectfd, (struct sockaddr *)&addr, sizeof(addr));//2. 等待connectfd连接成功(当可写时,也就是可以发送ACK时就表示连接成功了)struct epoll_event ev;ev.events |= EPOLLOUT;epoll_ctl(efd, EPOLL_CTL_ADD, connectfd, &ev);//3. 当connectfd写事件被触发,连接建立成功if (status == e_connecting && e->events & EPOLLOUT) {
status == e_connected; // 这里需要把写事件关闭 epoll_ctl(epfd, EPOLL_CTL_DEL, connectfd, NULL);}

连接断开

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

数据到达

// reactor 要用非阻塞ioif (e->events & EPOLLIN) {
//数据到达时也就是发生了EPOLLIN事件,这时就可以读取数据了 while (1) {
//一直读,直到读取完毕或者读取出错 int n = read(fd, buf, sz); if (n < 0) {
if (errno == EINTR) continue; if (errno == EWOULDBLOCK) //缓冲区中的数据都已经读完了 break; close(fd); //读取出错,关闭专线 } else if (n == 0) {
close_read(fd); // close(fd); } // 业务逻辑 }}

服务端数据发送给对端

int n = write(fd, buf, dz);if (n == -1) {
if (errno == EINTR) continue; if (errno == EWOULDBLOCK) {
//缓冲区已经满了 struct epoll_event ev; ev.events = EPOLLOUT; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); } close(fd);}// ...if (e->events & EPOLLOUT) {
int n = write(fd, buf, sz); //... if (n > 0) {
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); }}

reactor应用

单 reactor

在这里插入图片描述

多 reactor (one eventloop per thread)

多线程

在这里插入图片描述

多进程

在这里插入图片描述

转载地址:https://blog.csdn.net/zhizhengguan/article/details/122944857 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:cmake:CMAKE_EXPORT_COMPILE_COMMANDS选项
下一篇:Unix/Linux编程:UDP可靠传输协议之KCP的实现原理与应用

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月06日 13时20分41秒