فهرست منبع

Add examples/tcp_client_test.c

ithewei 3 سال پیش
والد
کامیت
8f11a32d25
8فایلهای تغییر یافته به همراه275 افزوده شده و 10 حذف شده
  1. 4 0
      Makefile
  2. 1 1
      README-CN.md
  3. 1 1
      README.md
  4. 4 2
      event/hloop.c
  5. 4 4
      event/hloop.h
  6. 4 0
      examples/CMakeLists.txt
  7. 2 2
      examples/nc.c
  8. 255 0
      examples/tcp_client_test.c

+ 4 - 0
Makefile

@@ -54,6 +54,7 @@ all: libhv examples
 
 
 examples: hmain_test htimer_test hloop_test \
 examples: hmain_test htimer_test hloop_test \
 	nc nmap tinyhttpd tinyproxyd httpd curl wget wrk consul \
 	nc nmap tinyhttpd tinyproxyd httpd curl wget wrk consul \
+	tcp_client_test \
 	tcp_echo_server \
 	tcp_echo_server \
 	tcp_chat_server \
 	tcp_chat_server \
 	tcp_proxy_server \
 	tcp_proxy_server \
@@ -103,6 +104,9 @@ htimer_test: prepare
 hloop_test: prepare
 hloop_test: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/hloop_test.c"
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/hloop_test.c"
 
 
+tcp_client_test: prepare
+	$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_client_test.c"
+
 tcp_echo_server: prepare
 tcp_echo_server: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_echo_server.c"
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_echo_server.c"
 
 

+ 1 - 1
README-CN.md

@@ -182,7 +182,7 @@ int main() {
 不想自定义协议和拆包组包的可直接使用现成的`HTTP/WebSocket`协议。<br>
 不想自定义协议和拆包组包的可直接使用现成的`HTTP/WebSocket`协议。<br>
 
 
 #### TCP客户端
 #### TCP客户端
-**c版本**: [examples/nc.c](examples/nc.c)
+**c版本**: [examples/tcp_client_test.c](examples/tcp_client_test.c)
 
 
 **c++版本**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
 **c++版本**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
 ```c++
 ```c++

+ 1 - 1
README.md

@@ -145,7 +145,7 @@ int main() {
 ```
 ```
 
 
 #### tcp client
 #### tcp client
-**c version**: [examples/nc.c](examples/nc.c)
+**c version**: [examples/tcp_client_test.c](examples/tcp_client_test.c)
 
 
 **c++ version**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
 **c++ version**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
 ```c++
 ```c++

+ 4 - 2
event/hloop.c

@@ -980,10 +980,11 @@ hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, hacce
     return io;
     return io;
 }
 }
 
 
-hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb) {
+hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb) {
     hio_t* io = hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
     hio_t* io = hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
     if (io == NULL) return NULL;
     if (io == NULL) return NULL;
     hio_setcb_connect(io, connect_cb);
     hio_setcb_connect(io, connect_cb);
+    hio_setcb_close(io, close_cb);
     if (hio_connect(io) != 0) return NULL;
     if (hio_connect(io) != 0) return NULL;
     return io;
     return io;
 }
 }
@@ -996,10 +997,11 @@ hio_t* hloop_create_ssl_server (hloop_t* loop, const char* host, int port, hacce
     return io;
     return io;
 }
 }
 
 
-hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb) {
+hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb) {
     hio_t* io = hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE);
     hio_t* io = hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE);
     if (io == NULL) return NULL;
     if (io == NULL) return NULL;
     hio_setcb_connect(io, connect_cb);
     hio_setcb_connect(io, connect_cb);
+    hio_setcb_close(io, close_cb);
     if (hio_connect(io) != 0) return NULL;
     if (hio_connect(io) != 0) return NULL;
     return io;
     return io;
 }
 }

+ 4 - 4
event/hloop.h

@@ -410,17 +410,17 @@ HV_EXPORT hio_t* hio_create_socket(hloop_t* loop, const char* host, int port,
 // @see examples/tcp_echo_server.c
 // @see examples/tcp_echo_server.c
 HV_EXPORT hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
 HV_EXPORT hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
 
 
-// @tcp_client: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_connect
+// @tcp_client: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
 // @see examples/nc.c
 // @see examples/nc.c
-HV_EXPORT hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb);
+HV_EXPORT hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
 
 
 // @ssl_server: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
 // @ssl_server: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
 // @see examples/tcp_echo_server.c => #define TEST_SSL 1
 // @see examples/tcp_echo_server.c => #define TEST_SSL 1
 HV_EXPORT hio_t* hloop_create_ssl_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
 HV_EXPORT hio_t* hloop_create_ssl_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
 
 
-// @ssl_client: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_connect
+// @ssl_client: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
 // @see examples/nc.c => #define TEST_SSL 1
 // @see examples/nc.c => #define TEST_SSL 1
-HV_EXPORT hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb);
+HV_EXPORT hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
 
 
 // @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_SERVER_SIDE)
 // @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_SERVER_SIDE)
 // @see examples/udp_echo_server.c
 // @see examples/udp_echo_server.c

+ 4 - 0
examples/CMakeLists.txt

@@ -4,6 +4,7 @@ list(APPEND EXAMPLES
     nc
     nc
     tinyhttpd
     tinyhttpd
     tinyproxyd
     tinyproxyd
+    tcp_client_test
     tcp_echo_server
     tcp_echo_server
     tcp_chat_server
     tcp_chat_server
     tcp_proxy_server
     tcp_proxy_server
@@ -31,6 +32,9 @@ target_link_libraries(tinyhttpd ${HV_LIBRARIES})
 add_executable(tinyproxyd tinyproxyd.c)
 add_executable(tinyproxyd tinyproxyd.c)
 target_link_libraries(tinyproxyd ${HV_LIBRARIES})
 target_link_libraries(tinyproxyd ${HV_LIBRARIES})
 
 
+add_executable(tcp_client_test tcp_client_test.c)
+target_link_libraries(tcp_client_test ${HV_LIBRARIES})
+
 add_executable(tcp_echo_server tcp_echo_server.c)
 add_executable(tcp_echo_server tcp_echo_server.c)
 target_link_libraries(tcp_echo_server ${HV_LIBRARIES})
 target_link_libraries(tcp_echo_server ${HV_LIBRARIES})
 
 

+ 2 - 2
examples/nc.c

@@ -198,10 +198,10 @@ Examples: nc 127.0.0.1 80\n\
     if (protocol == 1) {
     if (protocol == 1) {
 #if TEST_SSL
 #if TEST_SSL
         // ssl
         // ssl
-        sockio = hloop_create_ssl_client(loop, host, port, on_connect);
+        sockio = hloop_create_ssl_client(loop, host, port, on_connect, on_close);
 #else
 #else
         // tcp
         // tcp
-        sockio = hloop_create_tcp_client(loop, host, port, on_connect);
+        sockio = hloop_create_tcp_client(loop, host, port, on_connect, on_close);
 #endif
 #endif
     }
     }
     else if (protocol == 2) {
     else if (protocol == 2) {

+ 255 - 0
examples/tcp_client_test.c

@@ -0,0 +1,255 @@
+/*
+ * tcp client demo
+ *
+ * @build   make examples
+ * @server  bin/tcp_echo_server 1234
+ * @client  bin/tcp_client_test 127.0.0.1 1234
+ *
+ */
+
+#include "hloop.h"
+#include "hssl.h"
+#include "hmutex.h"
+
+#include "hbase.h"
+#include "herr.h"
+
+#define TEST_SSL        0
+#define TEST_UNPACK     0
+#define TEST_RECONNECT  1
+
+// @see mqtt/mqtt_client.h
+typedef struct tcp_client_s {
+    // connect: host:port
+    char host[256];
+    int  port;
+    int  connect_timeout; // ms
+    // reconnect
+    reconn_setting_t* reconn_setting;
+    // flags
+    unsigned char   ssl: 1; // Read Only
+    unsigned char   alloced_ssl_ctx: 1; // intern
+    unsigned char   connected : 1;
+    // privdata
+    hloop_t*    loop;
+    hio_t*      io;
+    htimer_t*   reconn_timer;
+    // SSL/TLS
+    hssl_ctx_t  ssl_ctx;
+    // thread-safe
+    hmutex_t    mutex_;
+    // ...
+} tcp_client_t;
+
+static tcp_client_t* tcp_client_new(hloop_t* loop DEFAULT(NULL));
+static void          tcp_client_run (tcp_client_t* cli);
+static void          tcp_client_stop(tcp_client_t* cli);
+static void          tcp_client_free(tcp_client_t* cli);
+
+// SSL/TLS
+static int tcp_client_set_ssl_ctx(tcp_client_t* cli, hssl_ctx_t ssl_ctx);
+static int tcp_client_new_ssl_ctx(tcp_client_t* cli, hssl_ctx_opt_t* opt);
+
+// reconnect
+static int tcp_client_set_reconnect(tcp_client_t* cli, reconn_setting_t* reconn);
+static int tcp_client_reconnect(tcp_client_t* cli);
+
+static void tcp_client_set_connnect_timeout(tcp_client_t* cli, int timeout_ms);
+static int  tcp_client_connect(tcp_client_t* cli, const char* host, int port, int ssl);
+static int  tcp_client_disconnect(tcp_client_t* cli);
+static bool tcp_client_is_connected(tcp_client_t* cli);
+
+static int  tcp_client_send(tcp_client_t* cli, const void* buf, int len);
+
+static void reconnect_timer_cb(htimer_t* timer) {
+    tcp_client_t* cli = (tcp_client_t*)hevent_userdata(timer);
+    if (cli == NULL) return;
+    cli->reconn_timer = NULL;
+    tcp_client_reconnect(cli);
+}
+
+static void on_close(hio_t* io) {
+    printf("onclose: connfd=%d error=%d\n", hio_fd(io), hio_error(io));
+    tcp_client_t* cli = (tcp_client_t*)hevent_userdata(io);
+    cli->connected = 0;
+    // reconnect
+    if (cli->reconn_setting && reconn_setting_can_retry(cli->reconn_setting)) {
+        uint32_t delay = reconn_setting_calc_delay(cli->reconn_setting);
+        printf("reconnect cnt=%d, delay=%d ...\n", cli->reconn_setting->cur_retry_cnt, cli->reconn_setting->cur_delay);
+        cli->reconn_timer = htimer_add(cli->loop, reconnect_timer_cb, delay, 1);
+        hevent_set_userdata(cli->reconn_timer, cli);
+    }
+}
+
+static void on_message(hio_t* io, void* buf, int len) {
+    printf("onmessage: %.*s\n", len, (char*)buf);
+    tcp_client_t* cli = (tcp_client_t*)hevent_userdata(io);
+    // ...
+}
+
+static void on_connect(hio_t* io) {
+    printf("onconnect: connfd=%d\n", hio_fd(io));
+    tcp_client_t* cli = (tcp_client_t*)hevent_userdata(io);
+    cli->connected = 1;
+
+#if TEST_UNPACK
+    static unpack_setting_t s_unpack_setting;
+    s_unpack_setting.mode = UNPACK_BY_DELIMITER;
+    s_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
+    s_unpack_setting.delimiter_bytes = 2;
+    s_unpack_setting.delimiter[0] = '\r';
+    s_unpack_setting.delimiter[1] = '\n';
+    hio_set_unpack(io, &s_unpack_setting);
+#endif
+
+    hio_write(io, "hello\r\n", 7);
+
+    hio_setcb_read(io, on_message);
+    hio_read(io);
+}
+
+// hloop_new -> malloc(tcp_client_t)
+tcp_client_t* tcp_client_new(hloop_t* loop) {
+    if (loop == NULL) {
+        loop = hloop_new(HLOOP_FLAG_AUTO_FREE);
+        if (loop == NULL) return NULL;
+    }
+    tcp_client_t* cli = NULL;
+    HV_ALLOC_SIZEOF(cli);
+    if (cli == NULL) return NULL;
+    cli->loop = loop;
+    hmutex_init(&cli->mutex_);
+    return cli;
+}
+
+// hloop_free -> free(tcp_client_t)
+void tcp_client_free(tcp_client_t* cli) {
+    if (!cli) return;
+    hmutex_destroy(&cli->mutex_);
+    if (cli->reconn_timer) {
+        htimer_del(cli->reconn_timer);
+        cli->reconn_timer = NULL;
+    }
+    if (cli->ssl_ctx && cli->alloced_ssl_ctx) {
+        hssl_ctx_free(cli->ssl_ctx);
+        cli->ssl_ctx = NULL;
+    }
+    HV_FREE(cli->reconn_setting);
+    HV_FREE(cli);
+}
+
+void tcp_client_run (tcp_client_t* cli) {
+    if (!cli || !cli->loop) return;
+    hloop_run(cli->loop);
+}
+
+void tcp_client_stop(tcp_client_t* cli) {
+    if (!cli || !cli->loop) return;
+    hloop_stop(cli->loop);
+}
+
+int tcp_client_set_ssl_ctx(tcp_client_t* cli, hssl_ctx_t ssl_ctx) {
+    cli->ssl_ctx = ssl_ctx;
+    return 0;
+}
+
+// hssl_ctx_new(opt) -> tcp_client_set_ssl_ctx
+int tcp_client_new_ssl_ctx(tcp_client_t* cli, hssl_ctx_opt_t* opt) {
+    opt->endpoint = HSSL_CLIENT;
+    hssl_ctx_t ssl_ctx = hssl_ctx_new(opt);
+    if (ssl_ctx == NULL) return ERR_NEW_SSL_CTX;
+    cli->alloced_ssl_ctx = true;
+    return tcp_client_set_ssl_ctx(cli, ssl_ctx);
+}
+
+int tcp_client_set_reconnect(tcp_client_t* cli, reconn_setting_t* reconn) {
+    if (reconn == NULL) {
+        HV_FREE(cli->reconn_setting);
+        return 0;
+    }
+    if (cli->reconn_setting == NULL) {
+        HV_ALLOC_SIZEOF(cli->reconn_setting);
+    }
+    *cli->reconn_setting = *reconn;
+    return 0;
+}
+
+int tcp_client_reconnect(tcp_client_t* cli) {
+    tcp_client_connect(cli, cli->host, cli->port, cli->ssl);
+    return 0;
+}
+
+int tcp_client_connect(tcp_client_t* cli, const char* host, int port, int ssl) {
+    if (!cli) return -1;
+    hv_strncpy(cli->host, host, sizeof(cli->host));
+    cli->port = port;
+    cli->ssl = ssl;
+    hio_t* io = hio_create_socket(cli->loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
+    if (io == NULL) return -1;
+    if (ssl) {
+        if (cli->ssl_ctx) {
+            hio_set_ssl_ctx(io, cli->ssl_ctx);
+        }
+        hio_enable_ssl(io);
+    }
+    if (cli->connect_timeout > 0) {
+        hio_set_connect_timeout(io, cli->connect_timeout);
+    }
+    cli->io = io;
+    hevent_set_userdata(io, cli);
+    hio_setcb_connect(io, on_connect);
+    hio_setcb_close(io, on_close);
+    return hio_connect(io);
+}
+
+int tcp_client_disconnect(tcp_client_t* cli) {
+    if (!cli || !cli->io) return -1;
+    // cancel reconnect first
+    tcp_client_set_reconnect(cli, NULL);
+    return hio_close(cli->io);
+}
+
+bool tcp_client_is_connected(tcp_client_t* cli) {
+    return cli && cli->connected;
+}
+
+int tcp_client_send(tcp_client_t* cli, const void* buf, int len) {
+    if (!cli || !cli->io || !buf || len == 0) return -1;
+    if (!cli->connected) return -2;
+    // thread-safe
+    hmutex_lock(&cli->mutex_);
+    int nwrite = hio_write(cli->io, buf, len);
+    hmutex_unlock(&cli->mutex_);
+    return nwrite;
+}
+
+int main(int argc, char** argv) {
+    if (argc < 3) {
+        printf("Usage: %s host port\n", argv[0]);
+        return -10;
+    }
+    const char* host = argv[1];
+    int port = atoi(argv[2]);
+
+    tcp_client_t* cli = tcp_client_new(NULL);
+    if (!cli) return -20;
+
+#if TEST_RECONNECT
+    reconn_setting_t reconn;
+    reconn_setting_init(&reconn);
+    reconn.min_delay = 1000;
+    reconn.max_delay = 10000;
+    reconn.delay_policy = 2;
+    tcp_client_set_reconnect(cli, &reconn);
+#endif
+
+    int ssl = 0;
+#if TEST_SSL
+    ssl = 1;
+#endif
+    tcp_client_connect(cli, host, port, ssl);
+
+    tcp_client_run(cli);
+    tcp_client_free(cli);
+    return 0;
+}