1
0

main.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * kcptun client
  3. *
  4. * @build: ./configure --with-kcp && make clean && make kcptun examples
  5. * @tcp_server: bin/tcp_echo_server 1234
  6. * @kcptun_server: bin/kcptun_server -l :4000 -t 127.0.0.1:1234
  7. * @kcptun_client: bin/kcptun_client -l :8388 -r 127.0.0.1:4000
  8. * @tcp_client: bin/nc 127.0.0.1 8388
  9. * > hello
  10. * < hello
  11. */
  12. #define WITH_KCP 1
  13. #include "hversion.h"
  14. #include "hmain.h"
  15. #include "hsocket.h"
  16. #include "hloop.h"
  17. #include "hthread.h"
  18. #include "../smux/smux.h"
  19. // config
  20. static const char* localaddr = ":8388";
  21. static const char* remoteaddr = "127.0.0.1:4000";
  22. static const char* mode = "fast";
  23. static int mtu = 1350;
  24. static int sndwnd = 128;
  25. static int rcvwnd = 512;
  26. // short options
  27. static const char options[] = "hvdl:r:m:";
  28. // long options
  29. static const option_t long_options[] = {
  30. {'h', "help", NO_ARGUMENT},
  31. {'v', "version", NO_ARGUMENT},
  32. {'d', "daemon", NO_ARGUMENT},
  33. {'l', "localaddr", REQUIRED_ARGUMENT},
  34. {'r', "remoteaddr", REQUIRED_ARGUMENT},
  35. {'m', "mode", REQUIRED_ARGUMENT},
  36. { 0, "mtu", REQUIRED_ARGUMENT},
  37. { 0, "sndwnd", REQUIRED_ARGUMENT},
  38. { 0, "rcvwnd", REQUIRED_ARGUMENT},
  39. };
  40. static const char detail_options[] = R"(
  41. -h|--help Print this information
  42. -v|--version Print version
  43. -d|--daemon Daemonize
  44. -l|--localaddr value local listen address (default: ":8388")
  45. -r|--remoteaddr value kcp server address (default: "127.0.0.1:4000")
  46. -m|--mode value profiles: fast3, fast2, fast, normal (default: "fast")
  47. --mtu value set maximum transmission unit for UDP packets (default: 1350)
  48. --sndwnd value set send window size(num of packets) (default: 128)
  49. --rcvwnd value set receive window size(num of packets) (default: 512)
  50. )";
  51. static void print_version() {
  52. printf("%s version %s\n", g_main_ctx.program_name, hv_compile_version());
  53. }
  54. static void print_help() {
  55. printf("Usage: %s [%s]\n", g_main_ctx.program_name, options);
  56. printf("Options:\n%s\n", detail_options);
  57. }
  58. static char listen_host[64] = "0.0.0.0";
  59. static int listen_port = 8388;
  60. static hio_t* listen_io = NULL;
  61. static kcp_setting_t s_kcp_setting;
  62. static char kcp_host[64] = "127.0.0.1";
  63. static int kcp_port = 4000;
  64. static hio_t* kcp_io = NULL;
  65. static smux_config_t smux_config;
  66. static smux_session_t smux_session;
  67. static smux_stream_t smux_stream0;
  68. static int verbose = 1;
  69. typedef struct kcp_ctx_s {
  70. smux_frame_t frame;
  71. uint16_t want_readbytes;
  72. } kcp_ctx_t;
  73. /* workflow:
  74. *
  75. * hloop_create_tcp_server ->
  76. * on_accept -> smux_session_open_stream ->
  77. * on_recv -> hio_write(kcp_io) ->
  78. * on_close -> smux_session_close_stream
  79. *
  80. * hloop_create_udp_client -> hio_set_kcp ->
  81. * on_recvfrom -> smux_session_get_stream -> hio_write(stream_io)
  82. *
  83. */
  84. static void send_hearbeat(htimer_t* timer) {
  85. smux_stream_t* smux_stream = (smux_stream_t*)hevent_userdata(timer);
  86. if (smux_stream == NULL) return;
  87. // NOP
  88. int packlen = smux_stream_output(smux_stream, SMUX_CMD_NOP);
  89. if (packlen > 0) {
  90. // printf("NOP %d\n", packlen);
  91. hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  92. }
  93. }
  94. static void on_close(hio_t* io) {
  95. // printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
  96. if (verbose) {
  97. char localaddrstr[SOCKADDR_STRLEN] = {0};
  98. char peeraddrstr[SOCKADDR_STRLEN] = {0};
  99. printf("disconnected connfd=%d [%s] <= [%s]\n", hio_fd(io),
  100. SOCKADDR_STR(hio_localaddr(io), localaddrstr),
  101. SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
  102. }
  103. smux_stream_t* smux_stream = (smux_stream_t*)hevent_userdata(io);
  104. if (smux_stream == NULL) return;
  105. // FIN
  106. int packlen = smux_stream_output(smux_stream, SMUX_CMD_FIN);
  107. if (packlen > 0) {
  108. // printf("FIN %d\n", packlen);
  109. hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  110. }
  111. // kill timer
  112. if (smux_stream->timer) {
  113. htimer_del(smux_stream->timer);
  114. smux_stream->timer = NULL;
  115. }
  116. // free buffer
  117. HV_FREE(smux_stream->rbuf.base);
  118. HV_FREE(smux_stream->wbuf.base);
  119. smux_session_close_stream(&smux_session, smux_stream->stream_id);
  120. hevent_set_userdata(io, NULL);
  121. }
  122. static void on_recv(hio_t* io, void* buf, int readbytes) {
  123. // printf("on_recv %.*s \n", readbytes, (char*)buf);
  124. smux_stream_t* smux_stream = (smux_stream_t*)hevent_userdata(io);
  125. if (smux_stream == NULL) return;
  126. // PSH
  127. smux_frame_t frame;
  128. smux_frame_init(&frame);
  129. frame.head.sid = smux_stream->stream_id;
  130. frame.head.cmd = SMUX_CMD_PSH;
  131. frame.head.length = readbytes;
  132. frame.data = (const char*)buf;
  133. int packlen = smux_frame_pack(&frame, smux_stream->wbuf.base, smux_stream->wbuf.len);
  134. if (packlen > 0) {
  135. // printf("PSH %d\n", packlen);
  136. int nwrite = hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  137. // printf("PSH ret=%d\n", nwrite);
  138. }
  139. }
  140. static void on_accept(hio_t* io) {
  141. // printf("on_accept connfd=%d\n", hio_fd(io));
  142. if (verbose) {
  143. char localaddrstr[SOCKADDR_STRLEN] = {0};
  144. char peeraddrstr[SOCKADDR_STRLEN] = {0};
  145. printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io),
  146. SOCKADDR_STR(hio_localaddr(io), localaddrstr),
  147. SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
  148. }
  149. hio_setcb_close(io, on_close);
  150. smux_stream_t* smux_stream = smux_session_open_stream(&smux_session, 0, io);
  151. // alloc buffer
  152. smux_stream->rbuf.len = mtu;
  153. smux_stream->wbuf.len = mtu;
  154. HV_ALLOC(smux_stream->rbuf.base, smux_stream->rbuf.len);
  155. HV_ALLOC(smux_stream->wbuf.base, smux_stream->wbuf.len);
  156. hio_set_readbuf(io, smux_stream->rbuf.base, smux_config.max_frame_size);
  157. /*
  158. // set heartbeat timer
  159. if (smux_config.keepalive_interval > 0) {
  160. smux_stream->timer = htimer_add(hevent_loop(io), send_hearbeat, smux_config.keepalive_interval, INFINITE);
  161. hevent_set_userdata(smux_stream->timer, smux_stream);
  162. }
  163. */
  164. hevent_set_userdata(io, smux_stream);
  165. // SYN
  166. int packlen = smux_stream_output(smux_stream, SMUX_CMD_SYN);
  167. if (packlen > 0) {
  168. // printf("SYN %d\n", packlen);
  169. hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  170. }
  171. hio_setcb_read(io, on_recv);
  172. hio_read(io);
  173. }
  174. static void on_kcp_recvfrom(hio_t* io, void* buf, int readbytes) {
  175. // printf("on_kcp_recvfrom %d\n", readbytes);
  176. kcp_ctx_t* kcp_ctx = (kcp_ctx_t*)hevent_userdata(io);
  177. smux_frame_t* frame = &kcp_ctx->frame;
  178. if (kcp_ctx->want_readbytes > 0) {
  179. frame->data = (const char*)buf;
  180. frame->head.length = readbytes;
  181. kcp_ctx->want_readbytes -= readbytes;
  182. }
  183. else {
  184. smux_frame_init(frame);
  185. int packlen = smux_frame_unpack(frame, buf, readbytes);
  186. if (packlen < 0 ||
  187. frame->head.version > 2 ||
  188. frame->head.cmd > SMUX_CMD_UPD) {
  189. fprintf(stderr, "smux_frame_unpack error: %d\n", packlen);
  190. return;
  191. }
  192. int datalen = packlen - SMUX_HEAD_LENGTH;
  193. if (datalen < frame->head.length) {
  194. kcp_ctx->want_readbytes = frame->head.length - datalen;
  195. frame->head.length = datalen;
  196. }
  197. }
  198. // printf("smux sid=%u cmd=%d length=%d\n", frame->head.sid, (int)frame->head.cmd, (int)frame->head.length);
  199. smux_stream_t* smux_stream = smux_session_get_stream(&smux_session, kcp_ctx->frame.head.sid);
  200. if (smux_stream == NULL) {
  201. if (frame->head.sid != 0 && frame->head.cmd != SMUX_CMD_FIN) {
  202. fprintf(stderr, "recvfrom invalid smux package!\n");
  203. }
  204. return;
  205. }
  206. switch (frame->head.cmd) {
  207. case SMUX_CMD_SYN:
  208. hio_setcb_read(smux_stream->io, on_recv);
  209. hio_read(smux_stream->io);
  210. break;
  211. case SMUX_CMD_FIN:
  212. hio_close(smux_stream->io);
  213. break;
  214. case SMUX_CMD_PSH:
  215. hio_write(smux_stream->io, frame->data, frame->head.length);
  216. break;
  217. case SMUX_CMD_NOP:
  218. break;
  219. default:
  220. break;
  221. }
  222. }
  223. int main(int argc, char** argv) {
  224. if (argc < 2) {
  225. print_help();
  226. exit(0);
  227. }
  228. // g_main_ctx
  229. main_ctx_init(argc, argv);
  230. //int ret = parse_opt(argc, argv, options);
  231. int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options));
  232. if (ret != 0) {
  233. print_help();
  234. exit(ret);
  235. }
  236. // help
  237. if (get_arg("h")) {
  238. print_help();
  239. exit(0);
  240. }
  241. // version
  242. if (get_arg("v")) {
  243. print_version();
  244. exit(0);
  245. }
  246. #ifdef OS_UNIX
  247. // daemon
  248. if (get_arg("d")) {
  249. // nochdir, noclose
  250. int ret = daemon(1, 1);
  251. if (ret != 0) {
  252. printf("daemon error: %d\n", ret);
  253. exit(-10);
  254. }
  255. }
  256. #endif
  257. const char* arg = get_arg("l");
  258. if (arg) {
  259. localaddr = arg;
  260. }
  261. arg = get_arg("r");
  262. if (arg) {
  263. remoteaddr = arg;
  264. }
  265. arg = get_arg("m");
  266. if (arg) {
  267. mode = arg;
  268. }
  269. arg = get_arg("mtu");
  270. if (arg) {
  271. mtu = atoi(arg);
  272. }
  273. arg = get_arg("sndwnd");
  274. if (arg) {
  275. sndwnd = atoi(arg);
  276. }
  277. arg = get_arg("rcvwnd");
  278. if (arg) {
  279. rcvwnd = atoi(arg);
  280. }
  281. const char* pos = strchr(localaddr, ':');
  282. int len = 0;
  283. if (pos) {
  284. len = pos - localaddr;
  285. if (len > 0) {
  286. memcpy(listen_host, localaddr, len);
  287. listen_host[len] = '\0';
  288. }
  289. listen_port = atoi(pos + 1);
  290. }
  291. pos = strchr(remoteaddr, ':');
  292. if (pos) {
  293. len = pos - remoteaddr;
  294. if (len > 0) {
  295. memcpy(kcp_host, remoteaddr, len);
  296. kcp_host[len] = '\0';
  297. }
  298. kcp_port = atoi(pos + 1);
  299. }
  300. if (strcmp(mode, "normal") == 0) {
  301. kcp_setting_init_with_normal_mode(&s_kcp_setting);
  302. } else if (strcmp(mode, "fast") == 0) {
  303. kcp_setting_init_with_fast_mode(&s_kcp_setting);
  304. } else if (strcmp(mode, "fast2") == 0) {
  305. kcp_setting_init_with_fast2_mode(&s_kcp_setting);
  306. } else if (strcmp(mode, "fast3") == 0) {
  307. kcp_setting_init_with_fast3_mode(&s_kcp_setting);
  308. } else {
  309. fprintf(stderr, "Unknown mode '%s'\n", mode);
  310. exit(-20);
  311. }
  312. s_kcp_setting.conv = hv_getpid();
  313. s_kcp_setting.mtu = mtu;
  314. s_kcp_setting.sndwnd = sndwnd;
  315. s_kcp_setting.rcvwnd = rcvwnd;
  316. printf("smux version: 1\n");
  317. printf("%s:%d => %s:%d\n", listen_host, listen_port, kcp_host, kcp_port);
  318. printf("mode: %s\n", mode);
  319. printf("mtu: %d\n", mtu);
  320. printf("sndwnd: %d rcvwnd: %d\n", sndwnd, rcvwnd);
  321. hloop_t* loop = hloop_new(0);
  322. listen_io = hloop_create_tcp_server(loop, listen_host, listen_port, on_accept);
  323. if (listen_io == NULL) {
  324. fprintf(stderr, "create tcp server error!\n");
  325. return -30;
  326. }
  327. kcp_io = hloop_create_udp_client(loop, kcp_host, kcp_port);
  328. if (kcp_io == NULL) {
  329. fprintf(stderr, "create udp client error!\n");
  330. return -40;
  331. }
  332. hio_set_kcp(kcp_io, &s_kcp_setting);
  333. kcp_ctx_t* kcp_ctx = NULL;
  334. HV_ALLOC_SIZEOF(kcp_ctx);
  335. hevent_set_userdata(kcp_io, kcp_ctx);
  336. hio_setcb_read(kcp_io, on_kcp_recvfrom);
  337. hio_read(kcp_io);
  338. // smux
  339. smux_config.max_frame_size = 1024;
  340. smux_session.next_stream_id = 1;
  341. smux_stream0.stream_id = 0;
  342. // set heartbeat timer
  343. if (smux_config.keepalive_interval > 0) {
  344. htimer_t* timer = htimer_add(loop, send_hearbeat, smux_config.keepalive_interval, INFINITE);
  345. hevent_set_userdata(timer, &smux_stream0);
  346. }
  347. hloop_run(loop);
  348. hloop_free(&loop);
  349. return 0;
  350. }