1
0

tcp_client_test.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * tcp client demo
  3. *
  4. * @build make examples
  5. * @server bin/tcp_echo_server 1234
  6. * @client bin/tcp_client_test 127.0.0.1 1234
  7. *
  8. */
  9. #include "hloop.h"
  10. #include "hssl.h"
  11. #include "hmutex.h"
  12. #include "hbase.h"
  13. #include "herr.h"
  14. #define TEST_SSL 0
  15. #define TEST_UNPACK 0
  16. #define TEST_RECONNECT 1
  17. // @see mqtt/mqtt_client.h
  18. typedef struct tcp_client_s {
  19. // connect: host:port
  20. char host[256];
  21. int port;
  22. int connect_timeout; // ms
  23. // reconnect
  24. reconn_setting_t* reconn_setting;
  25. // flags
  26. unsigned char ssl: 1; // Read Only
  27. unsigned char alloced_ssl_ctx: 1; // intern
  28. unsigned char connected : 1;
  29. // privdata
  30. hloop_t* loop;
  31. hio_t* io;
  32. htimer_t* reconn_timer;
  33. // SSL/TLS
  34. hssl_ctx_t ssl_ctx;
  35. // thread-safe
  36. hmutex_t mutex_;
  37. // ...
  38. } tcp_client_t;
  39. static tcp_client_t* tcp_client_new(hloop_t* loop DEFAULT(NULL));
  40. static void tcp_client_run (tcp_client_t* cli);
  41. static void tcp_client_stop(tcp_client_t* cli);
  42. static void tcp_client_free(tcp_client_t* cli);
  43. // SSL/TLS
  44. static int tcp_client_set_ssl_ctx(tcp_client_t* cli, hssl_ctx_t ssl_ctx);
  45. static int tcp_client_new_ssl_ctx(tcp_client_t* cli, hssl_ctx_opt_t* opt);
  46. // reconnect
  47. static int tcp_client_set_reconnect(tcp_client_t* cli, reconn_setting_t* reconn);
  48. static int tcp_client_reconnect(tcp_client_t* cli);
  49. static void tcp_client_set_connnect_timeout(tcp_client_t* cli, int timeout_ms);
  50. static int tcp_client_connect(tcp_client_t* cli, const char* host, int port, int ssl);
  51. static int tcp_client_disconnect(tcp_client_t* cli);
  52. static bool tcp_client_is_connected(tcp_client_t* cli);
  53. static int tcp_client_send(tcp_client_t* cli, const void* buf, int len);
  54. static void reconnect_timer_cb(htimer_t* timer) {
  55. tcp_client_t* cli = (tcp_client_t*)hevent_userdata(timer);
  56. if (cli == NULL) return;
  57. cli->reconn_timer = NULL;
  58. tcp_client_reconnect(cli);
  59. }
  60. static void on_close(hio_t* io) {
  61. printf("onclose: connfd=%d error=%d\n", hio_fd(io), hio_error(io));
  62. tcp_client_t* cli = (tcp_client_t*)hevent_userdata(io);
  63. cli->connected = 0;
  64. // reconnect
  65. if (cli->reconn_setting && reconn_setting_can_retry(cli->reconn_setting)) {
  66. uint32_t delay = reconn_setting_calc_delay(cli->reconn_setting);
  67. printf("reconnect cnt=%d, delay=%d ...\n", cli->reconn_setting->cur_retry_cnt, cli->reconn_setting->cur_delay);
  68. cli->reconn_timer = htimer_add(cli->loop, reconnect_timer_cb, delay, 1);
  69. hevent_set_userdata(cli->reconn_timer, cli);
  70. }
  71. }
  72. static void on_message(hio_t* io, void* buf, int len) {
  73. printf("onmessage: %.*s\n", len, (char*)buf);
  74. tcp_client_t* cli = (tcp_client_t*)hevent_userdata(io);
  75. // ...
  76. }
  77. static void on_connect(hio_t* io) {
  78. printf("onconnect: connfd=%d\n", hio_fd(io));
  79. tcp_client_t* cli = (tcp_client_t*)hevent_userdata(io);
  80. cli->connected = 1;
  81. #if TEST_UNPACK
  82. static unpack_setting_t s_unpack_setting;
  83. s_unpack_setting.mode = UNPACK_BY_DELIMITER;
  84. s_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
  85. s_unpack_setting.delimiter_bytes = 2;
  86. s_unpack_setting.delimiter[0] = '\r';
  87. s_unpack_setting.delimiter[1] = '\n';
  88. hio_set_unpack(io, &s_unpack_setting);
  89. #endif
  90. hio_write(io, "hello\r\n", 7);
  91. hio_setcb_read(io, on_message);
  92. hio_read(io);
  93. }
  94. // hloop_new -> malloc(tcp_client_t)
  95. tcp_client_t* tcp_client_new(hloop_t* loop) {
  96. if (loop == NULL) {
  97. loop = hloop_new(HLOOP_FLAG_AUTO_FREE);
  98. if (loop == NULL) return NULL;
  99. }
  100. tcp_client_t* cli = NULL;
  101. HV_ALLOC_SIZEOF(cli);
  102. if (cli == NULL) return NULL;
  103. cli->loop = loop;
  104. hmutex_init(&cli->mutex_);
  105. return cli;
  106. }
  107. // hloop_free -> free(tcp_client_t)
  108. void tcp_client_free(tcp_client_t* cli) {
  109. if (!cli) return;
  110. hmutex_destroy(&cli->mutex_);
  111. if (cli->reconn_timer) {
  112. htimer_del(cli->reconn_timer);
  113. cli->reconn_timer = NULL;
  114. }
  115. if (cli->ssl_ctx && cli->alloced_ssl_ctx) {
  116. hssl_ctx_free(cli->ssl_ctx);
  117. cli->ssl_ctx = NULL;
  118. }
  119. HV_FREE(cli->reconn_setting);
  120. HV_FREE(cli);
  121. }
  122. void tcp_client_run (tcp_client_t* cli) {
  123. if (!cli || !cli->loop) return;
  124. hloop_run(cli->loop);
  125. }
  126. void tcp_client_stop(tcp_client_t* cli) {
  127. if (!cli || !cli->loop) return;
  128. hloop_stop(cli->loop);
  129. }
  130. int tcp_client_set_ssl_ctx(tcp_client_t* cli, hssl_ctx_t ssl_ctx) {
  131. cli->ssl_ctx = ssl_ctx;
  132. return 0;
  133. }
  134. // hssl_ctx_new(opt) -> tcp_client_set_ssl_ctx
  135. int tcp_client_new_ssl_ctx(tcp_client_t* cli, hssl_ctx_opt_t* opt) {
  136. opt->endpoint = HSSL_CLIENT;
  137. hssl_ctx_t ssl_ctx = hssl_ctx_new(opt);
  138. if (ssl_ctx == NULL) return ERR_NEW_SSL_CTX;
  139. cli->alloced_ssl_ctx = true;
  140. return tcp_client_set_ssl_ctx(cli, ssl_ctx);
  141. }
  142. int tcp_client_set_reconnect(tcp_client_t* cli, reconn_setting_t* reconn) {
  143. if (reconn == NULL) {
  144. HV_FREE(cli->reconn_setting);
  145. return 0;
  146. }
  147. if (cli->reconn_setting == NULL) {
  148. HV_ALLOC_SIZEOF(cli->reconn_setting);
  149. }
  150. *cli->reconn_setting = *reconn;
  151. return 0;
  152. }
  153. int tcp_client_reconnect(tcp_client_t* cli) {
  154. tcp_client_connect(cli, cli->host, cli->port, cli->ssl);
  155. return 0;
  156. }
  157. int tcp_client_connect(tcp_client_t* cli, const char* host, int port, int ssl) {
  158. if (!cli) return -1;
  159. hv_strncpy(cli->host, host, sizeof(cli->host));
  160. cli->port = port;
  161. cli->ssl = ssl;
  162. hio_t* io = hio_create_socket(cli->loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
  163. if (io == NULL) return -1;
  164. if (ssl) {
  165. if (cli->ssl_ctx) {
  166. hio_set_ssl_ctx(io, cli->ssl_ctx);
  167. }
  168. hio_enable_ssl(io);
  169. }
  170. if (cli->connect_timeout > 0) {
  171. hio_set_connect_timeout(io, cli->connect_timeout);
  172. }
  173. cli->io = io;
  174. hevent_set_userdata(io, cli);
  175. hio_setcb_connect(io, on_connect);
  176. hio_setcb_close(io, on_close);
  177. return hio_connect(io);
  178. }
  179. int tcp_client_disconnect(tcp_client_t* cli) {
  180. if (!cli || !cli->io) return -1;
  181. // cancel reconnect first
  182. tcp_client_set_reconnect(cli, NULL);
  183. return hio_close(cli->io);
  184. }
  185. bool tcp_client_is_connected(tcp_client_t* cli) {
  186. return cli && cli->connected;
  187. }
  188. int tcp_client_send(tcp_client_t* cli, const void* buf, int len) {
  189. if (!cli || !cli->io || !buf || len == 0) return -1;
  190. if (!cli->connected) return -2;
  191. // thread-safe
  192. hmutex_lock(&cli->mutex_);
  193. int nwrite = hio_write(cli->io, buf, len);
  194. hmutex_unlock(&cli->mutex_);
  195. return nwrite;
  196. }
  197. int main(int argc, char** argv) {
  198. if (argc < 3) {
  199. printf("Usage: %s host port\n", argv[0]);
  200. return -10;
  201. }
  202. const char* host = argv[1];
  203. int port = atoi(argv[2]);
  204. tcp_client_t* cli = tcp_client_new(NULL);
  205. if (!cli) return -20;
  206. #if TEST_RECONNECT
  207. reconn_setting_t reconn;
  208. reconn_setting_init(&reconn);
  209. reconn.min_delay = 1000;
  210. reconn.max_delay = 10000;
  211. reconn.delay_policy = 2;
  212. tcp_client_set_reconnect(cli, &reconn);
  213. #endif
  214. int ssl = 0;
  215. #if TEST_SSL
  216. ssl = 1;
  217. #endif
  218. tcp_client_connect(cli, host, port, ssl);
  219. tcp_client_run(cli);
  220. tcp_client_free(cli);
  221. return 0;
  222. }