使用零拷贝技术实现消息转发功能
零拷贝技术介绍:史上最全零拷贝总结-CSDN博客
这是一个简单的基于epoll的Linux TCP代理程序,通过匿名管道和零拷贝技术的splice函数,将两个TCP端口相互连接,并转发数据。
#define _GNU_SOURCE 1
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include "list.h"#define err(x) perror(x), exit(1)
#define NEW(x) ((x) = xmalloc(sizeof(*(x))))
#define MAX(a,b) ((a) > (b) ? (a) : (b))int connection_timeout = 5; /* XXX configurable */void oom(void)
{fprintf(stderr, "Out of memory\n");exit(1);
}void *xmalloc(size_t size)
{void *p = calloc(size, 1);if (!p)oom();return p;
}void *xrealloc(void *old, size_t size)
{void *p = realloc(old, size);if (!p)oom();return p;
}struct addrinfo *resolve(char *name, char *port, int flags)
{int ret;struct addrinfo *adr;struct addrinfo hint = { .ai_flags = flags };ret = getaddrinfo(name, port, &hint, &adr);if (ret) {fprintf(stderr, "proxy: Cannot resolve %s %s: %s\n",name, port, gai_strerror(ret));exit(1);}return adr;
}void setnonblock(int fd, int *cache)
{int flags;if (!cache || *cache == -1) {flags = fcntl(fd, F_GETFL, 0);if (cache)*cache = flags;} elseflags = *cache;fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}struct buffer {int pipe[2];int bytes;
};struct conn {struct conn *other;int fd;struct buffer buf;time_t expire;struct list_head expire_node;
};LIST_HEAD(expire_list);#define MIN_EVENTS 32
struct epoll_event *events;
int num_events, max_events;int epoll_add(int efd, int fd, int revents, void *conn)
{struct epoll_event ev = { .events = revents, .data.ptr = conn };if (++num_events >= max_events) {max_events = MAX(max_events * 2, MIN_EVENTS);events = xrealloc(events, sizeof(struct epoll_event) * max_events);}return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
}int epoll_del(int efd, int fd)
{num_events--;assert(num_events >= 0);return epoll_ctl(efd, EPOLL_CTL_DEL, fd, (void *)1L);
}/* Create buffer between two connections */
struct buffer *newbuffer(struct buffer *buf)
{if (pipe2(buf->pipe, O_NONBLOCK) < 0) {perror("pipe");return NULL;}return buf;
}void delbuffer(struct buffer *buf)
{close(buf->pipe[0]);close(buf->pipe[1]);
}void delconn(int efd, struct conn *conn)
{list_del(&conn->expire_node);delbuffer(&conn->buf);epoll_del(efd, conn->fd);close(conn->fd);free(conn);
}struct conn *newconn(int efd, int fd, time_t now)
{struct conn *conn;NEW(conn);conn->fd = fd;if (!newbuffer(&conn->buf)) {delconn(efd, conn);return NULL;}if (epoll_add(efd, fd, EPOLLIN|EPOLLOUT|EPOLLET, conn) < 0) {perror("epoll");delconn(efd, conn);return NULL;}conn->expire = now + connection_timeout;list_add_tail(&conn->expire_node, &expire_list);return conn;
}/* Process incoming connection. */
void new_request(int efd, int lfd, int *cache, time_t now)
{int newsk = accept(lfd, NULL, NULL);if (newsk < 0) {perror("accept");return;}// xxx logsetnonblock(newsk, cache);newconn(efd, newsk, now);
}/* Open outgoing connection */
struct conn *
openconn(int efd, struct addrinfo *host, int *cache, struct conn *other,time_t now)
{int outfd = socket(host->ai_family, SOCK_STREAM, 0);if (outfd < 0)return NULL;setnonblock(outfd, cache);int n = connect(outfd, host->ai_addr, host->ai_addrlen);if (n < 0 && errno != EINPROGRESS) {perror("connect");close(outfd);return NULL;}struct conn *conn = newconn(efd, outfd, now);if (conn) {conn->other = other;other->other = conn;}return conn;
}#define BUFSZ 16384 /* XXX *//* Move from socket to pipe */
bool move_data_in(int srcfd, struct buffer *buf)
{for (;;) { int n = splice(srcfd, NULL, buf->pipe[1], NULL, BUFSZ, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);if (n > 0)buf->bytes += n;if (n == 0)return false;if (n < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK)return true;return false;}}return true;
}/* From pipe to socket */
bool move_data_out(struct buffer *buf, int dstfd)
{ while (buf->bytes > 0) {int bytes = buf->bytes;if (bytes > BUFSZ)bytes = BUFSZ;int n = splice(buf->pipe[0], NULL, dstfd, NULL,bytes, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);if (n == 0)break;if (n < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK)break;return false;}buf->bytes -= n;}/* bytes > 0, add dst to epoll set *//* else remove if it was added */return true;
}void closeconn(int efd, struct conn *conn)
{if (conn->other)delconn(efd, conn->other);delconn(efd, conn);
}int expire_connections(int efd, time_t now)
{struct conn *conn, *tmp;list_for_each_entry_safe (conn, tmp, &expire_list, expire_node) {if (conn->expire > now)return (conn->expire - now) * 1000;closeconn(efd, conn);}return -1;
}void touch_conn(struct conn *conn, time_t now)
{conn->expire = now + connection_timeout;list_del(&conn->expire_node);list_add_tail(&conn->expire_node, &expire_list);
}int listen_socket(int efd, char *lname, char *port)
{struct addrinfo *laddr = resolve(lname, port, AI_PASSIVE);int lfd = socket(laddr->ai_family, SOCK_STREAM, 0);if (lfd < 0) err("socket");int opt = 1;if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0) err("SO_REUSEADDR");if (bind(lfd, laddr->ai_addr, laddr->ai_addrlen) < 0) err("bind");if (listen(lfd, 20) < 0) err("listen");setnonblock(lfd, NULL);freeaddrinfo(laddr);if (epoll_add(efd, lfd, EPOLLIN, NULL) < 0) err("epoll add listen fd");return lfd;
}int main(int ac, char **av)
{if (ac != 4 && ac != 5) {fprintf(stderr,"Usage: proxy inport outhost outport [listenaddr]\n");exit(1);}struct addrinfo *outhost = resolve(av[2], av[3], 0);int efd = epoll_create(10);if (efd < 0) err("epoll_create");int lfd = listen_socket(efd, av[4] ? av[4] : "0.0.0.0", av[1]);int cache_in = -1, cache_out = -1; int timeo = -1;for (;;) {int nfds = epoll_wait(efd, events, num_events, timeo);if (nfds < 0) {perror("epoll");continue;}time_t now = time(NULL);int i;for (i = 0; i < nfds; i++) { struct epoll_event *ev = &events[i];struct conn *conn = ev->data.ptr;/* listen socket */if (conn == NULL) {if (ev->events & EPOLLIN)new_request(efd, lfd, &cache_in, now);continue;} if (ev->events & (EPOLLERR|EPOLLHUP)) {closeconn(efd, conn);continue;}struct conn *other = conn->other;/* No attempt for partial close right now */if (ev->events & EPOLLIN) {touch_conn(conn, now);if (!other)other = openconn(efd, outhost, &cache_out,conn, now);bool in = move_data_in(conn->fd, &conn->buf);bool out = move_data_out(&conn->buf, other->fd);if (!in || !out) { closeconn(efd, conn);continue;}touch_conn(other, now);} if ((ev->events & EPOLLOUT) && other) {if (!move_data_out(&other->buf, conn->fd))delconn(efd, conn);elsetouch_conn(conn, now);/* When the pipe filled up could havelost input events. Unfortunatelysplice doesn't tell us which endwas responsible for 0, so have to askexplicitely. */int len = 0;if (ioctl(other->fd, FIONREAD, &len) < 0)perror("ioctl");if (len > 0) {if (!move_data_in(other->fd, &other->buf))closeconn(efd, other);}}} timeo = expire_connections(efd, now);}return 0;
}相关文章:
使用零拷贝技术实现消息转发功能
零拷贝技术介绍:史上最全零拷贝总结-CSDN博客 这是一个简单的基于epoll的Linux TCP代理程序,通过匿名管道和零拷贝技术的splice函数,将两个TCP端口相互连接,并转发数据。 #define _GNU_SOURCE 1 #include <sys/socket.h> …...
【编程语言发展史】SQL的发展历史
目录 目录 SQL概述 SQL发展历史 SQL特点 SQL基本语句 SQL是结构化查询语言(Structure Query Language)的缩写,它是使用关系模型的数据库应用语言,由IBM在70年代开发出来,作为IBM关系数据库原型System R的原型关系语言,实现了…...
2023NOIP A层联测28-小猫吃火龙果
给你一个长为 n n n 的序列,每个位置是 A , B , C A,B,C A,B,C 三个中的一个物品。 A A A 吃 B B B, B B B 吃 C C C, C C C 吃 A A A。 现在有 m m m 次操作,每次操作有两种: 区间修改:给出 l , r…...
C# Dictionary与List的用法区别与联系
C#是一门广泛应用于软件开发的编程语言,其中Dictionary和List是两种常用的集合类型。它们在存储和操作数据时有着不同的特点和用途。本文将详细探讨C# Dictionary和List的用法区别与联系,并通过代码示例进行对比,以帮助读者更好地选择适合自己…...
Git应用(1)
一、Git Git(读音为/gɪt/。中文 饭桶 )是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。 了解更多可到GIT官网:Git - Downloads GIT一般工作流程如下: 1.从远程仓库中克隆 Git 资源作为本地…...
【Java】Netty创建网络服务端客户端(TCP/UDP)
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Netty创建网络服务端客户端示例。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更…...
Android 设计模式--单例模式
一,定义 单例模式就是确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例 二,使用场景 确保某个类只有一个对象的使用场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有…...
语音识别与自然语言处理(NLP):技术前沿与未来趋势
语音识别与自然语言处理(NLP):技术前沿与未来趋势 随着科技的快速发展,语音识别与自然语言处理(NLP)技术逐渐成为人工智能领域的研究热点。这两项技术的结合,使得机器能够更好地理解和处理人类语…...
k8s-docker二进制(1.28)的搭建
二进制文件-docker方式 1、准备的服务器 角色ip组件k8s-master1192.168.11.111kube-apiserver,kube-controller-manager,kube-scheduler,etcdk8s-master2192.168.11.112kube-apiserver,kube-controller-manager,kube-scheduler,etcdk8s-node1192.168.11.113kubelet,kube-prox…...
【代码随想录】算法训练计划18
1、513. 找树左下角的值 题目: 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 思路: 递归,规则,基本可以自己写出来 var maxDepth int var res int fun…...
Leetcode刷题详解—— 组合总和
1. 题目链接:39. 组合总和 2. 题目描述: 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些…...
Echarts柱状体实现滚动条动态滚动
当我们柱状图中X轴数据太多的时候,会自动把柱形的宽度挤的很细,带来的交互非常不好,因此就有一个属性来解决:dataZoom 第一种简易的版本,横向滚动。 dataZoom: {show: true, // 为true 滚动条出现realtime: true, // 实…...
SplayTree高分测试用例
测试用例结果展示 覆盖率 变异得分 测试注意点 从SplayTree测起,然后再测SubSplayTree,因为前者调用后者。SplaySubTree的remove方法大部分内容需要通过反射才能测到。value和index在SplayTree当中都不是唯一的。一个index可能对应多个value。 不足之…...
制作麒麟V10-server-sp2镜像
1.挂载iso 文件到目录 mount -o loop /xxx.iso /mnt 这样mnt 目录下会有iso 解压相关的文件 2.修改源文件内容 vim /etc/yum.repos.d/ kylin_x86_64.repo 将里面的所有的源enabled 都改成 0 并添加一个新的源 [ks10-local] name Kylin Linux Advanced Server 10 - Local base…...
2.docker镜像的导入导出
目录 概述docker 常用命令下载导出导入镜像结束 概述 docker 常用命令 本章节使用到的命令,总结在此,后面有使用案例。 命令作用docker images显示镜像docker rmi $(docker images -q)删除系统上所有的镜像docker rmi -f强制删除多个镜像 :…...
bs4介绍和遍历文档树、搜索文档树、案例:爬美女图片、 bs4其它用法、css选择器
bs4介绍和遍历文档树 BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库,解析库 需要安装模块:pip install beautifulsoup4 使用 解析库可以使用 lxml,速度快(必须安装) 可以使用python内置的 # html…...
微服务-开篇-个人对微服务的理解
从吃饭说起 个人理解新事物的时候喜欢将天上飞的理念转换成平常生活中的实践,对比理解这些高大上的名词,才能让我们减少恐慌的同时加深理解。废话不多说,我们从吃饭开始说起,逐渐类比出微服务的思想。 (个人见解&…...
机器学习算法-集成学习
概念 集成学习是一种机器学习方法,它通过构建并结合多个机器学习器(基学习器)来完成学习任务。集成学习的潜在思想是即便某一个弱分类器得到了错误的预测,其他的弱分类器也可以将错误纠正回来。集成学习通常被视为一种元算法&…...
LINUX入门篇【4】开发篇--开发工具vim的使用
前言: 从这一篇开始,我们将正式进入使用LINUX进行写程序和开发的阶段,可以说,由此开始,我们才开始真正去使用LINUX。 介绍工具: 1.LINUX软件包管理器yum: 1.yum的介绍: 在LINUX…...
代码随想录算法训练营Day 50 || 309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费
309.最佳买卖股票时机含冷冻期 力扣题目链接 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
