Giter Site home page Giter Site logo

cproxy's Introduction

项目介绍

CProxy是一个反向代理,用户可在自己内网环境中启动一个业务服务,并在同一网络下启动CProxyClient,用于向CProxyServer注册服务。CProxyClient和CProxyServer之间会创建一个隧道,外网可以通过访问CProxyServer,数据转发到CProxyClient,从而被业务服务接收到。实现内网服务被外网访问。

使用方法

bash build.sh
// 启动服务端
{ProjectDir}/build/server/Server --proxy_port=8090 --work_thread_nums=4
(另一个终端) 
// 启动客户端
{ProjectDir}/build/client/Client --local_server=127.0.0.1:7777 --cproxy_server=127.0.0.1:8080

项目亮点

  • 使用epoll作为IO多路复用的实现
  • 数据转发时,使用splice零拷贝,减少IO性能瓶颈
  • 数据连接和控制连接接耦,避免互相影响
  • 采用Reactor多线程模型,充分利用多核CPU性能

流程架构

角色

  1. LocalServer: 内网业务服务
  2. CProxyClient: CProxy客户端,一般与LocalServer部署在一起,对接CProxyServer和InnerServer
  3. CProxyServer: CProxy服务端
  4. PublicClient: 业务客户端

数据流

PublicClient先将请求打到CProxyServer,CProxyServer识别请求是属于哪个CProxyClient,然后将数据转发到CProxyClient,CProxyClient再识别请求是属于哪个LocalServer的,将请求再转发到LocalServer,完成数据的转发。 1

工作流程

先介绍CProxyServer端的两个概念:

  • Control:在CProxyServer中会维护一个ControlMap,一个Control对应一个CProxyClient,存储CProxyClient的一些元信息和控制信息
  • Tunnel:每个Control中会维护一个TunnelMap,一个Tunnel对应一个LocalServer服务

在CProxyClient端,也会维护一个TunnelMap,每个Tunnel对应一个LocalServer服务,只不过Client端的Tunnel与Server端的Tunnel存储的内容略有差异

启动流程

CProxyServer
  1. 完成几种工作线程的初始化。
  2. 监听一个CtlPort,等待CProxyClient连接。
CProxyClient
  1. 完成对应线程的初始化。
  2. 然后连接Server的CtlPort,此连接称为ctl_conn, 用于client和server之前控制信息的传递。
  3. 请求注册Control,获取ctl_id。
  4. 最后再根据Tunnel配置文件完成多个Tunnel的注册。需要注意的是,每注册一个Tunnel,Server端就会多监听一个PublicPort,作为外部访问LocalServer的入口。

数据转发流程

  1. Web上的PublicClient请求CProxyServer上的PublicPort建立连接;CProxyServer接收连接请求,将public_accept_fd封装成PublicConn。
  2. CProxyServer通过ctl_conn向client发送NotifyClientNeedProxyMsg通知Client需要创建一个proxy。
  3. Client收到后,会分别连接LocalServer和CProxyServer: 3.1. 连接LocalServer,将local_conn_fd封装成LocalConn。 3.2. 连接ProxyServer的ProxyPort,将proxy_conn_fd封装成ProxyConn,并将LocalConn和ProxyConn绑定。
  4. CProxyServer的ProxyPort收到请求后,将proxy_accept_fd封装成ProxyConn,将ProxyConn与PublicConn绑定。
  5. 此后的数据在PublicConn、ProxyConn和LocalConn上完成转发传输。

2

连接管理

复用proxy连接

为了避免频繁创建销毁proxy连接,在完成数据转发后,会将proxyConn放到空闲队列中,等待下次使用。 proxy_conn有两种模式 - 数据传输模式和空闲模式。在数据传输模式中,proxy_conn不会去读取解析缓冲区中的数据,只会把数据通过pipe管道转发到local_conn; 空闲模式时,会读取并解析缓冲区中的数据,此时的数据是一些控制信息,用于调整proxy_conn本身。

当有新publicClient连接时,会先从空闲列表中获取可用的proxy_conn,此时proxy_conn处于空闲模式,CProxyServer端会通过proxy_conn向CProxyClient端发送StartProxyConnReqMsg, CLient端收到后,会为这个proxy_conn绑定一个local_conn, 并将工作模式置为数据传输模式。之后数据在这对proxy_conn上进行转发。 3

数据连接断开处理

close和shutdown的区别

  1. close
int close(int sockfd)

在不考虑so_linger的情况下,close会关闭两个方向的数据流。

  1. 读方向上,内核会将套接字设置为不可读,任何读操作都会返回异常;
  2. 输出方向上,内核会尝试将发送缓冲区的数据发送给对端,之后发送fin包结束连接,这个过程中,往套接字写入数据都会返回异常。
  3. 若对端还发送数据过来,会返回一个rst报文。

注意:套接字会维护一个计数,当有一个进程持有,计数加一,close调用时会检查计数,只有当计数为0时,才会关闭连接,否则,只是将套接字的计数减一。 2. shutdown

int shutdown(int sockfd, int howto)

shutdown显得更加优雅,能控制只关闭连接的一个方向

  1. howto = 0 关闭连接的读方向,对该套接字进行读操作直接返回EOF;将接收缓冲区中的数据丢弃,之后再有数据到达,会对数据进行ACK,然后悄悄丢弃。
  2. howto = 1 关闭连接的写方向,会将发送缓冲区上的数据发送出去,然后发送fin包;应用程序对该套接字的写入操作会返回异常(shutdown不会检查套接字的计数情况,会直接关闭连接)
  3. howto = 2 0+1各操作一遍,关闭连接的两个方向。

项目使用shutdown去处理数据连接的断开,当CProxyServer收到publicClient的fin包(CProxyClient收到LocalServer的fin包)后,通过ctlConn通知对端, 对端收到后,调用shutdown(local_conn_fd/public_conn_fd, 2)关闭写方向。等收到另一个方向的fin包后,将proxyConn置为空闲模式,并放回空闲队列中。 4

在处理链接断开和复用代理链接这块遇到的坑比较多

  1. 控制对端去shutdown连接是通过ctl_conn去通知的,可能这一方向上对端的数据还没有全部转发完成就收到断开通知了,需要确保数据全部转发完才能调用shutdown去关闭连接。
  2. 从空闲列表中拿到一个proxy_conn后,需要发送StartProxyConnReq,告知对端开始工作,如果此时对端的这一proxy_conn还处于数据传输模式,就会报错了。

数据传输

数据在Server和Client都需进行转发,将数据从一个连接的接收缓冲区转发到另一个连接的发送缓冲区。如果使用write/read系统调用,整个流程如下图 5

数据先从内核空间复制到用户空间,之后再调用write系统调用将数据复制到内核空间。每次系统调用,都需要切换CPU上下文,而且,两次拷贝都需要CPU去执行(CPU copy),所以,大量的拷贝操作,会成为整个服务的性能瓶颈。

在CProxy中,使用splice的零拷贝方案,数据直接从内核空间的Source Socket Buffer转移到Dest Socket Buffer,不需要任何CPU copy。

6

splice通过pipe管道“传递”数据,基本原理是通过pipe管道修改source socket buffer和dest socket buffer的物理内存页

7

splice并不涉及数据的实际复制,只是修改了socket buffer的物理内存页指针。

并发模型

CProxyClient和CProxyServer均采用多线程reactor模型,利用线程池提高并发度。并使用epoll作为IO多路复用的实现方式。每个线程都有一个事件循环(One loop per thread)。线程分多类,各自处理不同的连接读写。

CProxyServer端

为了避免业务连接处理影响到Client和Server之间控制信息的传递。我们将业务数据处理与控制数据处理解耦。在Server端中设置了三种线程:

  1. mainThread: 用于监听ctl_conn和proxy_conn的连接请求以及ctl_conn上的相关读写
  2. publicListenThread: 监听并接收外来连接
  3. eventLoopThreadPool: 线程池,用于处理public_conn和proxy_conn之间的数据交换。

CProxyClient端

client端比较简单,只有两种线程:

  1. mainThread: 用于处理ctl_conn的读写
  2. eventLoopThreadPool: 线程池,用于处理proxy_conn和local_conn之间的数据交换

8 9

cproxy's People

Contributors

huiwan-code avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

cproxy's Issues

msg len too log

server端一直打印msg too long, 且没有数据转发

ctl_port:8080; proxy_port:8090; work_thread_nums: 4
[/home/zr/c_c++/CProxy-main/server/Server.cpp:86 15:22:01:940 +08:00] [I] [thread 12408] ctl_accept_fd: 11; from:127.0.0.1; port:35796
LRF4Z
[/home/zr/c_c++/CProxy-main/server/Server.cpp:109 15:23:21:981 +08:00] [I] [thread 12408] proxy_accept_fd: 13; from:192.168.3.13; port:14856
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:981 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long
[/home/zr/c_c++/CProxy-main/lib/proxy_conn.cpp:52 15:23:21:982 +08:00] [C] [thread 12410] msg len too long

请问为啥cmake报错呀

./build.sh 
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- CMAKE_CXX_FLAGS: -g;-Wall
-- CMAKE_CXX_FLAGS: -g -Wall
CMake Warning at CMakeLists.txt:42 (message):
  couldn't find clang-tidy.


-- Configuring done
CMake Error at lib/CMakeLists.txt:14 (add_library):
  Cannot find source file:

    buffer.cpp

  Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
  .hxx .in .txx


CMake Error: Cannot determine link language for target "lib".
CMake Error: CMake can not determine linker language for target: lib
-- Generating done
-- Build files have been written to: /home/zr/c_c++/CProxy-main/build
Scanning dependencies of target lib
make[2]: *** 没有规则可制作目标“lib/CMakeFiles/lib.dir/build”。 停止。
CMakeFiles/Makefile2:117: recipe for target 'lib/CMakeFiles/lib.dir/all' failed
make[1]: *** [lib/CMakeFiles/lib.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.