全球主机交流论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

CeraNetworks网络延迟测速工具IP归属甄别会员请立即修改密码
查看: 2905|回复: 26

[美国VPS] [破事水] 一键提升trojan-server性能

[复制链接]
发表于 2022-5-9 23:00:45 | 显示全部楼层 |阅读模式
本帖最后由 dunce 于 2022-5-9 23:30 编辑

trojan-高墙性能有点弱,原因在于用std::string作buffer,传输的时候触发了各种隐式拷贝构造。
我昨天闲得无聊做了个补丁,用来节省服务端处理TCP连接时的内存拷贝。因为实现得比较脏的缘故,就不去提pr了,放出来给大伙玩玩。

仅改动了TCP服务端:

1. 避免每次读和写都会触发的额外2次拷贝构造(传入string&以及make_shared,每次拷贝8kb)
2. 避免解析协议头时传入函数时的一次拷贝,没有优化掉保存解析结果时的拷贝(因为它保存到类里面去了)
3. 第一次写的时候从解析结果内move出buffer,而不是copy.

trojan.patch.ini (8.45 KB, 下载次数: 35)

食用方式:

克隆仓库
  1. git clone https://github.com/trojan-高墙/trojan.git
复制代码

把以下内容保存为trojan.patch,放到trojan文件夹里。

打上补丁
  1. git apply trojan.patch
复制代码

编译
  1. mkdir build && cd build
  2. cmake -DENABLE_MYSQL=OFF ..
  3. make -j
复制代码

  1. diff --git a/Dockerfile b/Dockerfile
  2. index 2656e73..5f782cb 100644
  3. --- a/Dockerfile
  4. +++ b/Dockerfile
  5. @@ -1,21 +1,20 @@
  6. -FROM alpine:3.11
  7. -
  8. -COPY . trojan
  9. -RUN apk add --no-cache --virtual .build-deps \
  10. +FROM alpine:latest AS builder
  11. +COPY . /trojan
  12. +RUN apk add --no-cache \
  13. +        boost-dev \
  14.          build-base \
  15.          cmake \
  16. -        boost-dev \
  17. -        openssl-dev \
  18.          mariadb-connector-c-dev \
  19. -    && (cd trojan && cmake . && make -j $(nproc) && strip -s trojan \
  20. -    && mv trojan /usr/local/bin) \
  21. -    && rm -rf trojan \
  22. -    && apk del .build-deps \
  23. -    && apk add --no-cache --virtual .trojan-rundeps \
  24. -        libstdc++ \
  25. -        boost-system \
  26. +        openssl-dev \
  27. +    && (cd /trojan && cmake . && make -j $(nproc) && strip -s trojan)
  28. +
  29. +FROM alpine:latest
  30. +RUN apk add --no-cache \
  31.          boost-program_options \
  32. +        boost-system \
  33. +        libstdc++ \
  34.          mariadb-connector-c
  35. +COPY --from=builder /trojan/trojan /usr/local/bin/trojan

  36. WORKDIR /config
  37. -CMD ["trojan", "config.json"]
  38. +ENTRYPOINT ["/usr/local/bin/trojan", "/config/config.json"]
  39. \ No newline at end of file
  40. diff --git a/src/proto/trojanrequest.cpp b/src/proto/trojanrequest.cpp
  41. index b0a6214..4638934 100644
  42. --- a/src/proto/trojanrequest.cpp
  43. +++ b/src/proto/trojanrequest.cpp
  44. @@ -40,6 +40,34 @@ int TrojanRequest::parse(const string &data) {
  45.      return data.length();
  46. }

  47. +int TrojanRequest::parse(const char *data, size_t length) {
  48. +    size_t first = length;
  49. +    for (size_t idx = 0; idx < length-1; ++idx) {
  50. +        if (data[idx] == '\r' && data[idx+1] == '\n') {
  51. +            first = idx;
  52. +            break;
  53. +        }
  54. +    }
  55. +    if (first == length) {
  56. +        return -1;
  57. +    }
  58. +
  59. +    password = string(data, first);
  60. +    // painfully slow
  61. +    payload = string(data+first+2, length-first-2);
  62. +    if (payload.length() == 0 || (payload[0] != CONNECT && payload[0] != UDP_ASSOCIATE)) {
  63. +        return -1;
  64. +    }
  65. +    command = static_cast<Command>(payload[0]);
  66. +    size_t address_len;
  67. +    bool is_addr_valid = address.parse(payload.substr(1), address_len);
  68. +    if (!is_addr_valid || payload.length() < address_len + 3 || payload.substr(address_len + 1, 2) != "\r\n") {
  69. +        return -1;
  70. +    }
  71. +    payload = payload.substr(address_len + 3);
  72. +    return length;
  73. +}
  74. +
  75. string TrojanRequest::generate(const string &password, const string &domainname, uint16_t port, bool tcp) {
  76.      string ret = password + "\r\n";
  77.      if (tcp) {
  78. diff --git a/src/proto/trojanrequest.h b/src/proto/trojanrequest.h
  79. index 2ac453d..6999782 100644
  80. --- a/src/proto/trojanrequest.h
  81. +++ b/src/proto/trojanrequest.h
  82. @@ -32,6 +32,7 @@ public:
  83.      SOCKS5Address address;
  84.      std::string payload;
  85.      int parse(const std::string &data);
  86. +    int parse(const char *data, size_t length);
  87.      static std::string generate(const std::string &password, const std::string &domainname, uint16_t port, bool tcp);
  88. };

  89. diff --git a/src/session/serversession.cpp b/src/session/serversession.cpp
  90. index fd4bc98..208e16c 100644
  91. --- a/src/session/serversession.cpp
  92. +++ b/src/session/serversession.cpp
  93. @@ -70,10 +70,11 @@ void ServerSession::in_async_read() {
  94.              destroy();
  95.              return;
  96.          }
  97. -        in_recv(string((const char*)in_read_buf, length));
  98. +        in_recv((char*)in_read_buf, length);
  99.      });
  100. }

  101. +// used by udp
  102. void ServerSession::in_async_write(const string &data) {
  103.      auto self = shared_from_this();
  104.      auto data_copy = make_shared<string>(data);
  105. @@ -86,6 +87,19 @@ void ServerSession::in_async_write(const string &data) {
  106.      });
  107. }

  108. +// used by tcp
  109. +void ServerSession::in_async_write(const char *data, size_t length) {
  110. +    auto self = shared_from_this();
  111. +    boost::asio::async_write(in_socket, boost::asio::buffer(data, length), [this, self](
  112. +        const boost::system::error_code error, size_t) {
  113. +        if (error) {
  114. +            destroy();
  115. +            return;
  116. +        }
  117. +        in_sent();
  118. +    });
  119. +}
  120. +
  121. void ServerSession::out_async_read() {
  122.      auto self = shared_from_this();
  123.      out_socket.async_read_some(boost::asio::buffer(out_read_buf, MAX_LENGTH), [this, self](const boost::system::error_code error, size_t length) {
  124. @@ -93,14 +107,14 @@ void ServerSession::out_async_read() {
  125.              destroy();
  126.              return;
  127.          }
  128. -        out_recv(string((const char*)out_read_buf, length));
  129. +        out_recv((char*)out_read_buf, length);
  130.      });
  131. }

  132. -void ServerSession::out_async_write(const string &data) {
  133. +void ServerSession::out_async_write(const char *data, size_t length) {
  134.      auto self = shared_from_this();
  135. -    auto data_copy = make_shared<string>(data);
  136. -    boost::asio::async_write(out_socket, boost::asio::buffer(*data_copy), [this, self, data_copy](const boost::system::error_code error, size_t) {
  137. +    boost::asio::async_write(out_socket, boost::asio::buffer(data, length), [this, self](
  138. +        const boost::system::error_code error, size_t) {
  139.          if (error) {
  140.              destroy();
  141.              return;
  142. @@ -132,10 +146,10 @@ void ServerSession::udp_async_write(const string &data, const udp::endpoint &end
  143.      });
  144. }

  145. -void ServerSession::in_recv(const string &data) {
  146. +void ServerSession::in_recv(const char *data, size_t length) {
  147.      if (status == HANDSHAKE) {
  148.          TrojanRequest req;
  149. -        bool valid = req.parse(data) != -1;
  150. +        bool valid = req.parse(data, length) != -1;
  151.          if (valid) {
  152.              auto password_iterator = config.password.find(req.password);
  153.              if (password_iterator == config.password.end()) {
  154. @@ -167,7 +181,7 @@ void ServerSession::in_recv(const string &data) {
  155.              return it == config.ssl.alpn_port_override.end() ? config.remote_port : it->second;
  156.          }());
  157.          if (valid) {
  158. -            out_write_buf = req.payload;
  159. +            out_write_buf = std::move(req.payload);
  160.              if (req.command == TrojanRequest::UDP_ASSOCIATE) {
  161.                  Log::log_with_endpoint(in_endpoint, "requested UDP associate to " + req.address.address + ':' + to_string(req.address.port), Log::INFO);
  162.                  status = UDP_FORWARD;
  163. @@ -179,7 +193,8 @@ void ServerSession::in_recv(const string &data) {
  164.              }
  165.          } else {
  166.              Log::log_with_endpoint(in_endpoint, "not trojan request, connecting to " + query_addr + ':' + query_port, Log::WARN);
  167. -            out_write_buf = data;
  168. +            // painfully slow
  169. +            out_write_buf = string(data, length);
  170.          }
  171.          sent_len += out_write_buf.length();
  172.          auto self = shared_from_this();
  173. @@ -229,17 +244,18 @@ void ServerSession::in_recv(const string &data) {
  174.                  status = FORWARD;
  175.                  out_async_read();
  176.                  if (!out_write_buf.empty()) {
  177. -                    out_async_write(out_write_buf);
  178. +                    out_async_write(out_write_buf.c_str(), out_write_buf.length());
  179.                  } else {
  180.                      in_async_read();
  181.                  }
  182.              });
  183.          });
  184.      } else if (status == FORWARD) {
  185. -        sent_len += data.length();
  186. -        out_async_write(data);
  187. +        sent_len += length;
  188. +        out_async_write(data, length);
  189.      } else if (status == UDP_FORWARD) {
  190. -        udp_data_buf += data;
  191. +        // painfully slow
  192. +        udp_data_buf += string(data, length);
  193.          udp_sent();
  194.      }
  195. }
  196. @@ -252,10 +268,10 @@ void ServerSession::in_sent() {
  197.      }
  198. }

  199. -void ServerSession::out_recv(const string &data) {
  200. +void ServerSession::out_recv(const char *data, size_t length) {
  201.      if (status == FORWARD) {
  202. -        recv_len += data.length();
  203. -        in_async_write(data);
  204. +        recv_len += length;
  205. +        in_async_write(data, length);
  206.      }
  207. }

  208. diff --git a/src/session/serversession.h b/src/session/serversession.h
  209. index c351f28..2b2fe1b 100644
  210. --- a/src/session/serversession.h
  211. +++ b/src/session/serversession.h
  212. @@ -40,12 +40,15 @@ private:
  213.      const std::string &plain_http_response;
  214.      void destroy();
  215.      void in_async_read();
  216. +    // used by udp
  217.      void in_async_write(const std::string &data);
  218. -    void in_recv(const std::string &data);
  219. +    // used by tcp
  220. +    void in_async_write(const char *data, size_t length);
  221. +    void in_recv(const char *data, size_t length);
  222.      void in_sent();
  223.      void out_async_read();
  224. -    void out_async_write(const std::string &data);
  225. -    void out_recv(const std::string &data);
  226. +    void out_async_write(const char *data, size_t length);
  227. +    void out_recv(const char *data, size_t length);
  228.      void out_sent();
  229.      void udp_async_read();
  230.      void udp_async_write(const std::string &data, const boost::asio::ip::udp::endpoint &endpoint);
复制代码
 楼主| 发表于 2022-5-10 13:50:56 来自手机 | 显示全部楼层
hd8 发表于 2022-5-10 13:49
性能瓶颈在线路,内存优化都是微秒级的

话是这么说没错,但是trojan内部无意义的拷贝也太多了
 楼主| 发表于 2022-5-9 23:30:48 | 显示全部楼层
把帖子权限去掉了,没人回复好无聊捏
发表于 2022-5-9 23:03:55 来自手机 | 显示全部楼层
确实有点慢,正好443被cf用了,搭在非标端口,基本不用
发表于 2022-5-9 23:05:04 | 显示全部楼层
MJJ卧虎藏龙啊
 楼主| 发表于 2022-5-9 23:05:10 | 显示全部楼层

要改动的地方太多了,只改TCP服务端的话代码看上去就不对称了,影响观感
发表于 2022-5-9 23:06:25 | 显示全部楼层
这个真技术贴,看不懂
 楼主| 发表于 2022-5-9 23:15:12 来自手机 | 显示全部楼层
dockerfile用的是dev分支最近的一次提交
发表于 2022-5-9 23:31:09 来自手机 | 显示全部楼层
太好了,是技术贴
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|全球主机交流论坛

GMT+8, 2024-4-28 14:40 , Processed in 0.069945 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表