使用零拷贝技术实现消息转发功能

零拷贝技术介绍:史上最全零拷贝总结-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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/138657.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端安全方面有哪些攻击方式?

在前端开发中&#xff0c;存在一些常见的安全攻击方式。以下是一些常见的前端安全攻击方式&#xff1a; 1&#xff1a;跨站脚本攻击&#xff08;XSS&#xff09;&#xff1a; XSS攻击是指攻击者通过在网页中注入恶意脚本&#xff0c;使得用户在浏览器中执行该脚本&#xff0c;…

C语言 每日一题 PTA 11.7 day13

1.求e的近似值 自然常数 e 可以用级数 1 1 / 1! 1 / 2! ⋯ 1 / n! ⋯ 来近似计算。 本题要求对给定的非负整数 n&#xff0c;求该级数的前 n 1 项和。 代码实现 #include<stdio.h> void main() {int a, i, j; double b 1; double c 1;printf("请输入一个数\n…

Linux RPM包安装、卸载和升级

我们以安装 apache 程序为例。因为后续章节还会介绍使用源码包的方式安装 apache 程序&#xff0c;读者可以直观地感受到源码包和 RPM 包的区别。 RPM包默认安装路径 通常情况下&#xff0c;RPM 包采用系统默认的安装路径&#xff0c;所有安装文件会按照类别分散安装到表 1 所…

如何使用 GTX750 或 1050 显卡安装 CUDA11+

前言 由于兼容性问题&#xff0c;使得我们若想用较新版本的 PyTorch&#xff0c;通过 GPU 方式训练模型&#xff0c;也得更换较新版本得 CUDA 工具包。然而 CUDA 的版本又与电脑显卡的驱动程序版本关联&#xff0c;如果是低版本的显卡驱动程序安装 CUDA11 及以上肯定会失败。 比…

Java TCP服务端多线程接收RFID网络读卡器上传数据

本示例使用设备介绍&#xff1a;WIFI/TCP/UDP/HTTP协议RFID液显网络读卡器可二次开发语音播报POE-淘宝网 (taobao.com) import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; impor…

计算机毕设 基于机器学习的文本聚类 - 可用于舆情分析

文章目录 0 简介1 项目介绍1.1 提取文本特征1.2 聚类算法选择 2 代码实现2.1 中文文本预处理2.2 特征提取2.2.1 Tf-idf2.2.2 word2vec 2.3 聚类算法2.3.1 k-means 2.3.2 DBSCAN2.4 实现效果2.4.1 tf-idf k-means聚类结果2.4.2 word2vec k-means 聚类结果 最后 0 简介 今天学…

抢量双11!抖音商城「官方立减」 缘何成为“爆单神器”?

10月20日抖音商城双11好物节正式开跑&#xff0c;仅仅三天&#xff0c;抖音商城整体GMV对比去年同期提升了200%&#xff0c;而在开跑一周后&#xff0c;一些品牌的销售额已经超过了今年整个618&#xff0c;可谓增势迅猛。其中&#xff0c;平台官方特别推出的「官方立减」玩法&a…

acwing算法基础之搜索与图论--kruskal算法

目录 1 基础知识2 模板3 工程化 1 基础知识 kruskal算法的关键步骤为&#xff1a; 将所有边按照权重从小到大排序。定义集合S&#xff0c;表示生成树。枚举每条边(a,b,c)&#xff0c;起点a&#xff0c;终点b&#xff0c;边长c。如果结点a和结点b不连通&#xff08;用并查集来…

K8S篇之Pod一直处于Pending状态原因解析

常见现象&#xff1a;Pod一直处于Pending状态。 废话不多说&#xff0c;直接上料。 可能原因1&#xff1a;Pod未被调度到节点上。 当 Pod 一直处于 Pending 状态时&#xff0c;说明该 Pod 还未被调度到某个节点上&#xff0c;需查看 Pod 分析问题原因。 例如执行&#xff1a…

计算机网络技术

深入浅出计算机网络 微课视频_哔哩哔哩_bilibili 第一章概述 1.1 信息时代的计算机网络 1. 计算机网络各类应用 2. 计算机网络带来的负面问题 3. 我国互联网发展情况 1.2 因特网概述 1. 网络、互连网&#xff08;互联网&#xff09;与因特网的区别与关系 如图所示&#xff0…

【机器学习2】模型评估

模型评估主要分为离线评估和在线评估两个阶段。 针对分类、 排序、 回归、序列预测等不同类型的机器学习问题&#xff0c; 评估指标的选择也有所不同。 1 评估指标 1.1准确率 准确率是指分类正确的样本占总样本个数的比例 但是准确率存在明显的问题&#xff0c;比如当负样本…

【React】05.JSX语法使用上的细节

JSX构建视图的基础知识 JSX&#xff1a;javascript and xml&#xff08;html&#xff09;把JS和HTML标签混合在了一起[并不是我们之前玩的字符串标签] 1、vscode如何支持JSX语法[格式化、快捷提示…] 创建的js文件&#xff0c;把后缀名设置为jsx即可&#xff0c;这样js文件中就…

单词规律问题

给定一种规律 pattern 和一个字符串 s &#xff0c;判断 s 是否遵循相同的规律。 这里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。 示例1: 输入: pattern “abba”, s “dog cat cat d…

可以体现Python语法精妙的十个例子!

文章目录 前言1.for - else2.一颗星*和两颗星**3.三元表达式4.with - as5.列表推导式6.列表索引的各种骚操作7.lambda函数8.yield 以及生成器和迭代器9.装饰器10.巧用断言assertPython技术资源分享1、Python所有方向的学习路线2、学习软件3、精品书籍4、入门学习视频5、实战案例…

“目标值排列匹配“和“背包组合问题“的区别和leetcode例题详解

1 目标值排列匹配 1.1 从目标字符串的角度来看&#xff0c;LC139是一个排列问题&#xff0c;因为最终目标子串的各个字符的顺序是固定的&#xff1f; 当我们从目标字符串 s 的角度来看 LC139 “单词拆分” 问题&#xff0c;确实可以认为它涉及到排列的概念&#xff0c;但这种…

React组件通信:如何优雅地实现组件间的数据传递

在React应用中&#xff0c;组件通信是至关重要的一部分。通过合适的数据传递和交互方式&#xff0c;我们可以构建出更加灵活和高效的前端应用。本文将介绍React组件通信的各种方式&#xff0c;并提供代码实现&#xff0c;帮助你更好地理解和应用这些技术。 1. 使用props进行父子…

acwing算法基础之搜索与图论--prim算法

目录 1 基础知识2 模板3 工程化 1 基础知识 朴素版prim算法的关键步骤&#xff1a; 初始化距离数组dist&#xff0c;将其内的所有元素都设为正无穷大。定义集合S&#xff0c;表示生成树。循环n次&#xff1a;找到不在集合S中且距离集合S最近的结点t&#xff0c;用它去更新剩余…

Java Post请求参数格式为XML

方式一&#xff1a; public static void PostXml1(String url, String xml) throws IOException {OkHttpClient client new OkHttpClient().newBuilder().build();//okhttp3.MediaType mediaType okhttp3.MediaType.parse("application/xml");okhttp3.MediaType m…

MySQL -- mysql connect

MySQL – mysql connect 文章目录 MySQL -- mysql connect一、Connector/C 使用1.环境安装2.尝试链接mysql client 二、MySQL接口1.初始化2.链接数据库3.下发mysql命令4.获取执行结果5.关闭mysql链接6.在C语言中连接MySQL 三、MySQL图形化界面推荐 使用C接口库来进行连接 一、…

【SpringBoot】手写模拟SpringBoot核心流程

依赖包 新建一个工程&#xff0c;包含两个 module&#xff1a; springboot 模块&#xff0c;表示 springboot 源码实现&#xff1b;user 模块&#xff0c;表示业务系统&#xff0c;使用 springboot 模块&#xff1b; 依赖包&#xff1a;Spring、SpringMVC、Tomcat 等&#xff…