Forráskód Böngészése

Add examples: echo chat proxy

ithewei 5 éve
szülő
commit
1f68f57118

+ 18 - 7
Makefile

@@ -33,8 +33,13 @@ endif
 default: all
 all: libhv examples
 examples: hmain_test htimer_test hloop_test \
-	tcp udp nc nmap httpd curl \
-	http_server_test http_client_test consul_cli
+	nc nmap httpd curl \
+	udp_echo_server \
+	tcp_echo_server \
+	tcp_chat_server \
+	tcp_proxy_server \
+	http_server_test http_client_test \
+	consul_cli
 
 clean:
 	$(MAKEF) clean SRCDIRS="$(ALL_SRCDIRS)"
@@ -63,11 +68,17 @@ htimer_test: prepare
 hloop_test: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/hloop_test.c"
 
-tcp: prepare
-	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/tcp.c"
+udp_echo_server: prepare
+	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/udp_echo_server.c"
 
-udp: prepare
-	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/udp.c"
+tcp_echo_server: prepare
+	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/tcp_echo_server.c"
+
+tcp_chat_server: prepare
+	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/tcp_chat_server.c"
+
+tcp_proxy_server: prepare
+	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/tcp_proxy_server.c"
 
 nc: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/nc.c"
@@ -132,4 +143,4 @@ echo-servers:
 	$(CXX) -g -Wall -std=c++11 -o bin/poco_echo     echo-servers/poco_echo.cpp   -lPocoNet -lPocoUtil -lPocoFoundation
 	$(CXX) -g -Wall -std=c++11 -o bin/muduo_echo    echo-servers/muduo_echo.cpp  -lmuduo_net -lmuduo_base -lpthread
 
-.PHONY: clean prepare libhv install examples tcp udp nc nmap httpd curl consul_cli unittest webbench echo-servers
+.PHONY: clean prepare libhv install examples nc nmap httpd curl consul_cli unittest webbench echo-servers

+ 16 - 16
README.md

@@ -127,7 +127,7 @@ bin/webbench -c 2 -t 60 localhost:8080
 ![libhv-vs-nginx.png](html/downloads/libhv-vs-nginx.png)
 
 ### EventLoop
-see [examples/tcp.c](examples/tcp.c) [examples/udp.c](examples/udp.c) [examples/nc.c](examples/nc.c)
+see [examples/tcp_echo_server.c](examples/tcp_echo_server.c) [examples/udp_echo_server.c](examples/udp_echo_server.c) [examples/nc.c](examples/nc.c)
 ```c
 // TCP echo server
 #include "hloop.h"
@@ -163,16 +163,22 @@ int main(int argc, char** argv) {
 }
 ```
 ```shell
-make tcp udp nc
-bin/tcp 1111
-bin/nc 127.0.0.1 1111
+make examples
 
-bin/udp 2222
-bin/nc -u 127.0.0.1 2222
+bin/tcp_echo_server 1234
+bin/nc 127.0.0.1 1234
 
-make hloop_test
-bin/hloop_test
-bin/nc 127.0.0.1 10514
+bin/tcp_chat_server 1234
+bin/nc 127.0.0.1 1234
+bin/nc 127.0.0.1 1234
+
+bin/httpd -s restart -d
+bin/tcp_proxy_server 1234 127.0.0.1:8080
+bin/curl -v 127.0.0.1:8080
+bin/curl -v 127.0.0.1:1234
+
+bin/udp_echo_server 1234
+bin/nc -u 127.0.0.1 1234
 ```
 
 ## BUILD
@@ -184,12 +190,6 @@ see [BUILD.md](BUILD.md)
 
 ### examples
 - make examples
-    - make tcp   # tcp server
-    - make udp   # udp server
-    - make nc    # network client
-    - make nmap  # host discovery
-    - make httpd # http server
-    - make curl  # http client
 
 ### unittest
 - make unittest
@@ -232,7 +232,7 @@ bin/curl -v localhost:8080 --http2
 ```
 
 #### other options
-see config.mk
+see [config.mk](config.mk)
 
 ### echo-servers
 ```shell

+ 16 - 8
examples/CMakeLists.txt

@@ -2,10 +2,12 @@ list(APPEND EXAMPLES
     hmain_test
     hloop_test
     htimer_test
-    tcp
-    udp
     nc
     nmap
+    udp_echo_server
+    tcp_echo_server
+    tcp_chat_server
+    tcp_proxy_server
 )
 
 include_directories(.. ../base ../event ../utils)
@@ -19,12 +21,6 @@ target_link_libraries(hloop_test hv)
 add_executable(htimer_test htimer_test.c)
 target_link_libraries(htimer_test hv)
 
-add_executable(tcp tcp.c)
-target_link_libraries(tcp hv)
-
-add_executable(udp udp.c)
-target_link_libraries(udp hv)
-
 add_executable(nc nc.c)
 target_link_libraries(nc hv)
 
@@ -32,6 +28,18 @@ add_executable(nmap nmap.cpp)
 target_compile_definitions(nmap PRIVATE -DPRINT_DEBUG)
 target_link_libraries(nmap hv)
 
+add_executable(udp_echo_server udp_echo_server.c)
+target_link_libraries(udp_echo_server hv)
+
+add_executable(tcp_echo_server tcp_echo_server.c)
+target_link_libraries(tcp_echo_server hv)
+
+add_executable(tcp_chat_server tcp_chat_server.c)
+target_link_libraries(tcp_chat_server hv)
+
+add_executable(tcp_proxy_server tcp_proxy_server.c)
+target_link_libraries(tcp_proxy_server hv)
+
 if(WITH_HTTP)
     include_directories(../http)
 if(WITH_HTTP_SERVER)

+ 9 - 0
examples/curl.cpp

@@ -1,3 +1,12 @@
+/*
+ * @build: make examples
+ * @server bin/httpd -s restart -d
+ * @usage: bin/curl -v www.baidu.com
+ *         bin/curl -v 127.0.0.1:8080
+ *         bin/curl -v 127.0.0.1:8080/ping
+ *         bin/curl -v 127.0.0.1:8080/echo -d 'hello,world!'
+ */
+
 #include "http_client.h"
 
 #ifdef _MSC_VER

+ 8 - 0
examples/hloop_test.c

@@ -1,3 +1,11 @@
+/*
+ * @build: make examples
+ * @usage: bin/hloop_test
+ *         bin/nc 127.0.0.1 10514
+ *         nc     127.0.0.1 10514
+ *
+ */
+
 #include "hloop.h"
 #include "hbase.h"
 #include "hlog.h"

+ 6 - 6
examples/htimer_test.c

@@ -5,29 +5,33 @@ void on_timer(htimer_t* timer) {
     printf("time=%llus on_timer\n", LLU(hloop_now(hevent_loop(timer))));
 }
 
+// test htimer_add
 void on_timer_add(htimer_t* timer) {
     printf("time=%llus on_timer_add\n", LLU(hloop_now(hevent_loop(timer))));
     htimer_add(hevent_loop(timer), on_timer_add, 1000, 1);
 }
 
+// test htimer_del
 void on_timer_del(htimer_t* timer) {
     printf("time=%llus on_timer_del\n", LLU(hloop_now(hevent_loop(timer))));
     htimer_del(timer);
 }
 
+// test htimer_reset
 void on_timer_reset(htimer_t* timer) {
     printf("time=%llus on_timer_reset\n", LLU(hloop_now(hevent_loop(timer))));
     htimer_reset((htimer_t*)hevent_userdata(timer));
 }
 
+// test hloop_stop
 void on_timer_quit(htimer_t* timer) {
     printf("time=%llus on_timer_quit\n", LLU(hloop_now(hevent_loop(timer))));
     hloop_stop(hevent_loop(timer));
 }
 
+// test cron
 void cron_hourly(htimer_t* timer) {
-    time_t tt;
-    time(&tt);
+    time_t tt = time(NULL);
     printf("time=%llus cron_hourly: %s\n", LLU(hloop_now(hevent_loop(timer))), ctime(&tt));
 }
 
@@ -35,12 +39,8 @@ int main() {
     HV_MEMCHECK;
     hloop_t* loop = hloop_new(0);
 
-    // on_timer_add triggered forever
     htimer_add(loop, on_timer_add, 1000, 1);
-    // on_timer_del triggered just once
     htimer_add(loop, on_timer_del, 1000, 10);
-
-    // on_timer triggered after 10s
     htimer_t* reseted = htimer_add(loop, on_timer, 5000, 1);
     htimer_t* reset = htimer_add(loop, on_timer_reset, 1000, 5);
     hevent_set_userdata(reset, reseted);

+ 14 - 0
examples/nc.c

@@ -1,3 +1,17 @@
+/*
+ * network client
+ *
+ * @build:  make examples
+ * @server  bin/httpd -s restart -d
+ * @usage:  bin/nc 127.0.0.1 8080
+ *          > GET / HTTP/1.1
+ *          > Connection: close
+ *          > [Enter]
+ *          > GET / HTTP/1.1
+ *          > Connection: keep-alive
+ *          > [Enter]
+ */
+
 #include "hloop.h"
 #include "hbase.h"
 #include "hsocket.h"

+ 146 - 0
examples/tcp_chat_server.c

@@ -0,0 +1,146 @@
+/*
+ * tcp chat server
+ *
+ * @build   make examples
+ * @server  bin/tcp_chat_server 1234
+ * @clients bin/nc 127.0.0.1 1234
+ *          nc     127.0.0.1 1234
+ *          telnet 127.0.0.1 1234
+ */
+
+#include "hloop.h"
+#include "hsocket.h"
+#include "hbase.h"
+#include "list.h"
+
+typedef struct chatroom_s {
+    hloop_t*            loop;
+    hio_t*              listenio;
+    int                 roomid;
+    struct list_head    conns;
+} chatroom_t;
+
+typedef struct connection_s {
+    hio_t*              connio;
+    char                addr[SOCKADDR_STRLEN];
+    struct list_node    node;
+} connection_t;
+
+static chatroom_t s_chatroom;
+
+static void join(chatroom_t* room, connection_t* conn);
+static void leave(chatroom_t* room, connection_t* conn);
+static void broadcast(chatroom_t* room, const char* msg, int msglen);
+
+void join(chatroom_t* room, connection_t* conn) {
+    list_add(&conn->node, &room->conns);
+
+    char msg[256] = {0};
+    int msglen = 0;
+
+    struct list_node* node;
+    connection_t* cur;
+    msglen = snprintf(msg, sizeof(msg), "room[%d] clients:\r\n", room->roomid);
+    hio_write(conn->connio, msg, msglen);
+    list_for_each (node, &room->conns) {
+        cur = list_entry(node, connection_t, node);
+        msglen = snprintf(msg, sizeof(msg), "[%s]\r\n", cur->addr);
+        hio_write(conn->connio, msg, msglen);
+    }
+    hio_write(conn->connio, "\r\n", 2);
+
+    msglen = snprintf(msg, sizeof(msg), "client[%s] join room[%d]\r\n", conn->addr, room->roomid);
+    broadcast(room, msg, msglen);
+}
+
+void leave(chatroom_t* room, connection_t* conn) {
+    list_del(&conn->node);
+
+    char msg[256] = {0};
+    int msglen = snprintf(msg, sizeof(msg), "client[%s] leave room[%d]\r\n", conn->addr, room->roomid);
+    broadcast(room, msg, msglen);
+}
+
+void broadcast(chatroom_t* room, const char* msg, int msglen) {
+    printf("> %.*s", msglen, msg);
+    struct list_node* node;
+    connection_t* conn;
+    list_for_each (node, &room->conns) {
+        conn = list_entry(node, connection_t, node);
+        hio_write(conn->connio, msg, msglen);
+    }
+}
+
+static void on_close(hio_t* io) {
+    printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
+
+    connection_t* conn = (connection_t*)hevent_userdata(io);
+    if (conn) {
+        hevent_set_userdata(io, NULL);
+
+        leave(&s_chatroom, conn);
+        HV_FREE(conn);
+    }
+}
+
+static void on_recv(hio_t* io, void* buf, int readbytes) {
+    printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes);
+    char localaddrstr[SOCKADDR_STRLEN] = {0};
+    char peeraddrstr[SOCKADDR_STRLEN] = {0};
+    printf("[%s] <=> [%s]\n",
+            SOCKADDR_STR(hio_localaddr(io), localaddrstr),
+            SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
+    printf("< %.*s", readbytes, (char*)buf);
+
+    // broadcast
+    connection_t* conn = (connection_t*)hevent_userdata(io);
+    assert(conn != NULL);
+    char msg[256] = {0};
+    int msglen = snprintf(msg, sizeof(msg), "client[%s] say: %.*s", conn->addr, readbytes, (char*)buf);
+    broadcast(&s_chatroom, msg, msglen);
+}
+
+static void on_accept(hio_t* io) {
+    printf("on_accept connfd=%d\n", hio_fd(io));
+    char localaddrstr[SOCKADDR_STRLEN] = {0};
+    char peeraddrstr[SOCKADDR_STRLEN] = {0};
+    printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io),
+            SOCKADDR_STR(hio_localaddr(io), localaddrstr),
+            SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
+
+    hio_setcb_close(io, on_close);
+    hio_setcb_read(io, on_recv);
+    hio_read(io);
+
+    // free on_close
+    connection_t* conn = NULL;
+    HV_ALLOC_SIZEOF(conn);
+    conn->connio = io;
+    strcpy(conn->addr, peeraddrstr);
+    hevent_set_userdata(io, conn);
+    join(&s_chatroom, conn);
+}
+
+int main(int argc, char** argv) {
+    if (argc < 2) {
+        printf("Usage: tcp port\n");
+        return -10;
+    }
+    int port = atoi(argv[1]);
+
+    hloop_t* loop = hloop_new(0);
+    hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept);
+    if (listenio == NULL) {
+        return -20;
+    }
+    printf("listenfd=%d\n", hio_fd(listenio));
+
+    s_chatroom.loop = loop;
+    s_chatroom.listenio = listenio;
+    s_chatroom.roomid = random() % 1000000;
+    list_init(&s_chatroom.conns);
+
+    hloop_run(loop);
+    hloop_free(&loop);
+    return 0;
+}

+ 13 - 3
examples/tcp.c → examples/tcp_echo_server.c

@@ -1,11 +1,21 @@
+/*
+ * tcp echo server
+ *
+ * @build   make examples
+ * @server  bin/tcp_echo_server 1234
+ * @client  bin/nc 127.0.0.1 1234
+ *          nc     127.0.0.1 1234
+ *          telnet 127.0.0.1 1234
+ */
+
 #include "hloop.h"
 #include "hsocket.h"
 
-void on_close(hio_t* io) {
+static void on_close(hio_t* io) {
     printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
 }
 
-void on_recv(hio_t* io, void* buf, int readbytes) {
+static void on_recv(hio_t* io, void* buf, int readbytes) {
     printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes);
     char localaddrstr[SOCKADDR_STRLEN] = {0};
     char peeraddrstr[SOCKADDR_STRLEN] = {0};
@@ -18,7 +28,7 @@ void on_recv(hio_t* io, void* buf, int readbytes) {
     hio_write(io, buf, readbytes);
 }
 
-void on_accept(hio_t* io) {
+static void on_accept(hio_t* io) {
     printf("on_accept connfd=%d\n", hio_fd(io));
     char localaddrstr[SOCKADDR_STRLEN] = {0};
     char peeraddrstr[SOCKADDR_STRLEN] = {0};

+ 125 - 0
examples/tcp_proxy_server.c

@@ -0,0 +1,125 @@
+/*
+ * tcp proxy server
+ *
+ * @test:         make examples
+ * @http_server:  bin/httpd -d
+ * @proxy_server: bin/tcp_proxy_server 1234 127.0.0.1:8080
+ *                bin/tcp_proxy_server 1234 www.baidu.com
+ * @client:       bin/curl -v 127.0.0.1:1234
+ *                bin/nc 127.0.0.1 1234
+ *                > GET / HTTP/1.1
+ *                > Connection: close
+ *                > [Enter]
+ *                > GET / HTTP/1.1
+ *                > Connection: keep-alive
+ *                > [Enter]
+ */
+
+#include "hloop.h"
+#include "hsocket.h"
+
+// hloop_create_tcp_server => on_accept(connio) => proxyio = hloop_create_tcp_client
+// on_proxy_connect => hio_read(connio) hio_read(proxyio)
+// on_recv(connio) => hio_write(proxyio)
+// on_proxy_recv(proxyio) => hio_write(connio)
+// on_close(connio) => hio_close(proxyio)
+// on_proxy(proxyio) => hio_close(connio)
+
+static char proxy_host[64] = "127.0.0.1";
+static int proxy_port = 80;
+
+static void on_proxy_close(hio_t* proxyio) {
+    hio_t* connio = (hio_t*)hevent_userdata(proxyio);
+    if (connio) {
+        hevent_set_userdata(proxyio, NULL);
+        hio_close(connio);
+    }
+}
+
+static void on_proxy_recv(hio_t* proxyio, void* buf, int readbytes) {
+    hio_t* connio = (hio_t*)hevent_userdata(proxyio);
+    assert(connio != NULL);
+    hio_write(connio, buf, readbytes);
+}
+
+static void on_proxy_connect(hio_t* proxyio) {
+    hio_t* connio = (hio_t*)hevent_userdata(proxyio);
+    assert(connio != NULL);
+    hio_read(connio);
+
+    hio_setcb_close(proxyio, on_proxy_close);
+    hio_setcb_read(proxyio, on_proxy_recv);
+    hio_read(proxyio);
+}
+
+static void on_close(hio_t* io) {
+    printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
+    hio_t* proxyio = (hio_t*)hevent_userdata(io);
+    if (proxyio) {
+        hio_close(proxyio);
+    }
+}
+
+static void on_recv(hio_t* io, void* buf, int readbytes) {
+    printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes);
+    char localaddrstr[SOCKADDR_STRLEN] = {0};
+    char peeraddrstr[SOCKADDR_STRLEN] = {0};
+    printf("[%s] <=> [%s]\n",
+            SOCKADDR_STR(hio_localaddr(io), localaddrstr),
+            SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
+    printf("< %.*s", readbytes, (char*)buf);
+
+    hio_t* proxyio = (hio_t*)hevent_userdata(io);
+    assert(proxyio != NULL);
+    hio_write(proxyio, buf, readbytes);
+}
+
+static void on_accept(hio_t* io) {
+    printf("on_accept connfd=%d\n", hio_fd(io));
+    char localaddrstr[SOCKADDR_STRLEN] = {0};
+    char peeraddrstr[SOCKADDR_STRLEN] = {0};
+    printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io),
+            SOCKADDR_STR(hio_localaddr(io), localaddrstr),
+            SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
+
+    hio_setcb_read(io, on_recv);
+    hio_setcb_close(io, on_close);
+
+    hio_t* proxyio = hloop_create_tcp_client(hevent_loop(io), proxy_host, proxy_port, on_proxy_connect);
+    if (proxyio == NULL) {
+        hio_close(io);
+    }
+    hevent_set_userdata(proxyio, io);
+    hevent_set_userdata(io, proxyio);
+}
+
+int main(int argc, char** argv) {
+    if (argc < 3) {
+        printf("Usage: tcp port proxy_host:proxy_port\n");
+        return -10;
+    }
+    int port = atoi(argv[1]);
+    char* pos = strchr(argv[2], ':');
+    if (pos) {
+        int len = pos - argv[2];
+        if (len > 0) {
+            memcpy(proxy_host, argv[2], len);
+            proxy_host[len] = '\0';
+        }
+        proxy_port = atoi(pos + 1);
+    } else {
+        strncpy(proxy_host, argv[2], sizeof(proxy_host));
+    }
+    if (proxy_port == 0) proxy_port = 80;
+    printf("proxy: [%s:%d]\n", proxy_host, proxy_port);
+
+    hloop_t* loop = hloop_new(0);
+    hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept);
+    if (listenio == NULL) {
+        return -20;
+    }
+    printf("listenfd=%d\n", hio_fd(listenio));
+    hloop_run(loop);
+    hloop_free(&loop);
+    return 0;
+}

+ 12 - 2
examples/udp.c → examples/udp_echo_server.c

@@ -1,11 +1,21 @@
+/*
+ * udp echo server
+ *
+ * @build   make examples
+ * @server  bin/udp_echo_server 1234
+ * @client  bin/nc -u 127.0.0.1 1234
+ *          nc     -u 127.0.0.1 1234
+ *
+ */
+
 #include "hloop.h"
 #include "hsocket.h"
 
-void on_close(hio_t* io) {
+static void on_close(hio_t* io) {
     printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
 }
 
-void on_recvfrom(hio_t* io, void* buf, int readbytes) {
+static void on_recvfrom(hio_t* io, void* buf, int readbytes) {
     printf("on_recvfrom fd=%d readbytes=%d\n", hio_fd(io), readbytes);
     char localaddrstr[SOCKADDR_STRLEN] = {0};
     char peeraddrstr[SOCKADDR_STRLEN] = {0};

+ 16 - 16
readme_cn.md

@@ -125,7 +125,7 @@ bin/webbench -c 2 -t 60 localhost:8080
 ![libhv-vs-nginx.png](html/downloads/libhv-vs-nginx.png)
 
 ### EventLoop
-见[examples/tcp.c](examples/tcp.c) [examples/udp.c](examples/udp.c) [examples/nc.c](examples/nc.c)
+见[examples/tcp_echo_server.c](examples/tcp_echo_server.c) [examples/udp_echo_server.c](examples/udp_echo_server.c) [examples/nc.c](examples/nc.c)
 ```c++
 // TCP echo server
 #include "hloop.h"
@@ -161,16 +161,22 @@ int main(int argc, char** argv) {
 }
 ```
 ```shell
-make tcp udp nc
-bin/tcp 1111
-bin/nc 127.0.0.1 1111
+make examples
 
-bin/udp 2222
-bin/nc -u 127.0.0.1 2222
+bin/tcp_echo_server 1234
+bin/nc 127.0.0.1 1234
 
-make hloop_test
-bin/hloop_test
-bin/nc 127.0.0.1 10514
+bin/tcp_chat_server 1234
+bin/nc 127.0.0.1 1234
+bin/nc 127.0.0.1 1234
+
+bin/httpd -s restart -d
+bin/tcp_proxy_server 1234 127.0.0.1:8080
+bin/curl -v 127.0.0.1:8080
+bin/curl -v 127.0.0.1:1234
+
+bin/udp_echo_server 1234
+bin/nc -u 127.0.0.1 1234
 ```
 
 ## 构建
@@ -182,12 +188,6 @@ bin/nc 127.0.0.1 10514
 
 ### 示例
 - make examples
-    - make tcp   # tcp server
-    - make udp   # udp server
-    - make nc    # network client
-    - make nmap  # host discovery
-    - make httpd # http server
-    - make curl  # http client
 
 ### 单元测试
 - make unittest
@@ -230,7 +230,7 @@ bin/curl -v localhost:8080 --http2
 ```
 
 #### 更多选项
-见config.mk
+见[config.mk](config.mk)
 
 ### echo-servers
 ```shell