关闭

splice系统调用实现的TCP代理

6052人阅读 评论(2) 收藏 举报
正如Linus所说,splice实际上是内核空间的read/write,而tee则是内核空间的memcpy,至于sendfile,它只是一种特定的优化,该优化对于可以使用page cache的文件系统有效。
       Linus反复强调buffer,用来回答关于“为何splice和tee都要依赖管道”,也就是说,文件描述符的IO依赖于一种buffer,至于说sendfile为何没有使用buffer,那是因为只有适用于page cache的文件描述符才能使用sendfile,而page cache本身就可以作为一种buffer!
       为何不能做一个系统调用,可以直接将一个socket的数据发往另一个socket呢?不是说不能,而是为了API的稳定性,肯定不能单独为socket这种文件做一个splice,更加合理的是实现一个splice,其文件描述符可以是任意的文件描述符。然而如此一来就要在file_operations结构体里面添加新的适用于任意文件的钩子函数splice from/to,这样不是不可以,而是将一类文件和另一类文件通过splice关联了起来。比如从一个socket写到一个字符设备,第一,socket要知道该字符设备如何被写入,第二,字符设备要知道socket如何被读取...因此正规的splice使用了一个buffer,这就是pipe。总而言之,涉及到两个文件之间内容通信的话,必然需要一个buffer,这里的“必然”一词很有意思,不是基于能不能实现考虑,也不是基于性能考虑,而是基于API的稳定性以及设计的简单性考虑,用户态的read/write接口有一个参数就是buffer,splice虽然是内核态的,也需要一个buffer,内核态的现成buffer是什么呢?是pipe。
       以下是一个最简单的使用splice实现的TCP代理程序,如果不看上面扯的那些,可能对调用两次splice有点失望了,毕竟可以直接splice(sock1, NULL, sock2, NULL, 65535, 0)啊,可是必然需要一个buffer的话,就造一个内核buffer,即pipe,这样pipe+splice不再像buffer+read/write那样亲自干活了,它们变身为一个管理者,只发送指令,之后自己不用做事,静等内核完成内容的转移,没有看出效果的同学们,可能你们的数量量太小了,毕竟在内核空间copy内存和内核空间用户态之间copy内存是不同的,除了一系列的处理器检查之外,缺页处理也是一大户啊。下面是代码,没有异常处理,没有错误判断,直奔主题(splice代理即无极变速代理):
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <netdb.h>
#include <sys/socket.h>
int main(int argc,char *argv[])
{
        int fd, client_fd, server_fd, ret;
        int yes = 1;
        struct sockaddr_in addr = {0};
        fd = socket(AF_INET,SOCK_STREAM,0);
        addr.sin_family = AF_INET;
        addr.sin_port = htons(1234);
        addr.sin_addr.s_addr = inet_addr("192.168.4.29");
        ret = bind(fd,(struct sockaddr *)(&addr),sizeof(struct sockaddr));
        ret = setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes));
        ret = listen(fd,5);
        socklen_t len = sizeof(addr);
        client_fd = accept(fd,(struct sockaddr*)(&addr),&len);
        { //接收客户端,连接服务端。
                struct sockaddr_in addr = {0}, addr2= {0};
                struct in_addr x;
                inet_aton("192.168.4.28",&x);
                addr.sin_family = AF_INET;
                addr.sin_addr = x;
                addr.sin_port = htons(12345);
                server_fd = socket(AF_INET,SOCK_STREAM,0);
                ret = connect(server_fd,(struct sockaddr*)&addr,sizeof(addr));
        }
        { //将客户端数据转发到服务端
                struct pollfd pfds[2];
                int pfd[2] = {-1};
                int ret = -1;
                pfds[0].fd = client_fd;
                pfds[0].events = POLLIN|POLLOUT;
                pfds[1].fd = server_fd;
                pfds[1].events = POLLIN|POLLOUT;
                ret = pipe(pfd);
                while (1) {
                        int efds = -1;
                        if ((efds = poll(pfds,2,-1)) < 0) {
                                return -1;
                        }
                        //两次splice,第一次sock_client->pipe,第二次pipe->sock_server
                        if(pfds[0].revents & POLLIN) {
                                int rncount = splice(pfds[0].fd, NULL, pfd[1], NULL, 65535, SPLICE_F_MORE);
                                int wncount = splice(pfd[0], NULL, pfds[1].fd, NULL, 65535, SPLICE_F_MORE);
                        }
                }
        }
}
我不晓得有没有人使用这种方式实现,因为我只关注OpenVPN,但是我还是真觉得这种方式比较靠谱,因为所谓的代理有两种:
1.一种是为了去控制。代理不想让你直接的访问目标,需要做一些前置工作,比如ACL,比如流控等等。
2.另一种是为了摆脱控制。为了防止内部人员随意访问外部,封掉了端口,但是你可以使用代理访问外部。

除此之外,代理就是一个转发器,作为转发功能,和以上两点无关,而以上两点,完全可以在while之前完成!
0
0
查看评论

linux splice使用示例 (使用socket服务于单用户的回射服务器)

#define _GNU_SOURCE #include #include #include #include #include #include #include #include void check(int res, const char *fucname) { if(r...
  • wks19891215
  • wks19891215
  • 2016-03-12 16:03
  • 684

4.3 TCP Splice

分散-聚集IO是一种允许向skb的非连续空间(frag page)中写入数据,并利用网卡支持发送skb中非连续空间中的数据来实现减少数据copy次数,以提高数据发送效率的方法。sendfile系统调用就是利用这一功能来提升性能的。下面以sendfile系统调用为例说明分散-聚集IO功能的使用方法。 ...
  • u011130578
  • u011130578
  • 2015-03-24 22:08
  • 970

splice系列系统调用

关注splice系列系统调用(包括splice,tee和vmsplice)已经有一段时间了,开始的时候并未能领会splice的意义所在,致使得出了“splice系列系统调用不怎么实用”的错误结论。随着内核研究的深入,才逐渐懂得:splice对于其中一个文件描述符必须是管道的要求并不是阻碍其应用的障碍...
  • QQ276592716
  • QQ276592716
  • 2014-04-06 21:38
  • 988

Linux 的 splice 和sendfile系统调用

Linux内核有zero copy的函数。nginx和proftpd中用到sendfile(文件到socket),haproxy则用到slice(socket到socket),比较下来,haproxy仍然需要调用两次system call(与read,write一样),在网上没有找到相关的性能测试,...
  • jollyjumper
  • jollyjumper
  • 2014-03-31 23:15
  • 1737

Linux环境下基于条件约束的HTTP/TCP透明代理和流量牵引方案

一. 前言 代理技术,是针对客户端(Client)和服务器(Server)之间的通信交互而言的一种介入技术。依据代理方式的不同,大致可以分为两种:1. 非透明代理;2. 透明代理。 1.       非透明代理 即被代理的客户端知晓代理服...
  • cancanfairy
  • cancanfairy
  • 2016-03-11 16:48
  • 2523

TCP 系统调用

引言 典型的 TCP 客户机和服务器应用程序通过发布 TCP 系统调用序列来获取某些函数。这些系统调用包括socket ()、bind ()、listen ()、accept ()、send () 和 receive()。本文介绍在应用程序发布 TC...
  • q454684431
  • q454684431
  • 2016-03-24 17:06
  • 3349

sock代理服务原理(TCP穿透)

原文转自:http://www.cppblog.com/zuhd/archive/2010/06/08/117366.htmlsock代理分为sock4代理和 sock5代理。sock4支持TCP(事实仅支持TCP),无需用户名、密码验证;sock5支持TCP和UDP,根据代理服务器设置是否需要用户...
  • WHACKW
  • WHACKW
  • 2015-07-23 10:30
  • 12702

实现TCP透明代理(附下载)

本文所描述的TCP代理服务器工作于网络协议层次中的应用层,位于传输层之上。只要是以TCP的方式为客户提供服务的(包括我们的HTTP服务器,HTTP底层走的仍然是TCP),我们都可以在真正的TCP服务器前面增加代理服务器。
  • zhuweisky
  • zhuweisky
  • 2014-05-15 11:16
  • 17442

泛洪攻击(Flood)与TCP代理(TCP proxy)

下文摘自H3C攻击防范指导手册 泛洪攻击 网络上常常会发生泛洪攻击和网络扫描攻击。泛洪攻击指攻击者向攻击目标发送大量的虚假请求,驱使被攻击者由于不断应付这些无用信息而筋疲力尽,合法的用户却由此无法享受到相应服务,即发生拒绝服务。扫描攻击是攻击者对网络进行主机或端口扫描,通常攻击者通过扫描了解网...
  • Holmofy
  • Holmofy
  • 2017-03-29 12:31
  • 1014

PRX 通过LSP实现浏览器Socks5/Tcp代理(从发送数据上着手)

本文阐述针对市面上主流的浏览器 实现基于Socks5协议Tcp代理部分原理 它是浏览器翻墙的一种方法 这只是在LSP实现方式中一种类别 它具备很多不同方式 但在本文中不在累赘;此方法适应“Chrome、Firebox、IE、OperaWeb”浏览器 本文中给出的代码思路是利用C/C++实现的 并且不...
  • liulilittle
  • liulilittle
  • 2017-09-18 15:21
  • 717
    个人资料
    • 访问:7042039次
    • 积分:84507
    • 等级:
    • 排名:第18名
    • 原创:1434篇
    • 转载:2篇
    • 译文:0篇
    • 评论:3112条
    文章存档
    最新评论