From f599cc99be29c47a7765e3fae602f3474b30926a Mon Sep 17 00:00:00 2001
From: yinbin6 <yinbin8@huawei.com>
Date: Thu, 11 Jul 2024 10:30:31 +0800
Subject: [PATCH] example: sync example update

---
 build/build.sh                          |   8 +
 examples/FAULT_INJECT.md                |  33 ++
 examples/README.md                      |  79 +++-
 examples/inc/bussiness.h                |  16 +-
 examples/inc/client.h                   |  44 +-
 examples/inc/parameter.h                |  80 +++-
 examples/inc/server.h                   |  25 +-
 examples/inc/utilities.h                |  88 +++-
 examples/main.c                         |   7 +-
 examples/src/bussiness.c                | 212 ++++++---
 examples/src/client.c                   | 562 ++++++++++++++++++------
 examples/src/parameter.c                | 484 +++++++++++++++++---
 examples/src/server.c                   | 512 ++++++++++++++++-----
 examples/src/utilities.c                | 386 +++++++++++-----
 src/lstack/core/lstack_protocol_stack.c |   2 +-
 15 files changed, 2006 insertions(+), 532 deletions(-)
 create mode 100644 examples/FAULT_INJECT.md

diff --git a/build/build.sh b/build/build.sh
index 4464f8c..622e1cc 100755
--- a/build/build.sh
+++ b/build/build.sh
@@ -31,3 +31,11 @@ if [ $? -ne 0 ]; then
 fi
 
 cd -
+cd ../examples
+cmake .
+make
+if [ $? -ne 0 ]; then
+    echo "build examples failed"
+    exit 1
+fi
+cd -
diff --git a/examples/FAULT_INJECT.md b/examples/FAULT_INJECT.md
new file mode 100644
index 0000000..ff551a9
--- /dev/null
+++ b/examples/FAULT_INJECT.md
@@ -0,0 +1,33 @@
+# Gazelle 故障注入 说明
+
+## 需求
+1. example:构造黑盒故障
+   * 延迟类：accept|read：
+     * accept: 构造tcp_acceptmbox_full的情景.
+     * read: 构造tcp_refuse_count、recvmbox满
+   * 跳过类：跳过 read/write并close：
+     * read: 构造链接关闭时时4次挥手的情景,验证TCP状态机。
+2. gazelle/lwip: 构造白盒故障，支持注入故障报文、协议栈状态、事件设置、资源异常等
+   * 编译宏支持
+   * 提供接口：配置文件、env
+   * 故障报文注入：
+     * 类似内核tc工具：
+       * 内核TC工具qdisc指令原理：报文分组被添加到网卡队列（qdisc），该队列决定发包顺序。<br>
+       qdisc指令可以在队列层面实现延时、丢包、重复等故障。
+       * dpdk性能检测工具testpmd可以模拟实现类似的故障模拟，testpmd与gazelle不兼容，需要参考其中调用的dpdk接口来改gazelle代码。<br>
+       * 延时故障
+       * 丢包故障
+         - 思路：调整网卡队列，随机丢弃百分比的包，然后发送。
+         - 函数调用：rte_rand()，rte_eth_tx_burst()。
+       * 包重复故障
+       * 随机故障
+       * 乱序故障
+   * 协议栈状态故障
+     * ...
+   * 事件设置
+     * ...
+   * 资源异常
+     * 资源耗尽，无法申请。
+     * ...
+ 
+ 
diff --git a/examples/README.md b/examples/README.md
index 5a73ce0..77a0f85 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -7,6 +7,7 @@
 * 支持多线程网络非对称模型，一个 listen 线程，若干个读写线程。listen 线程和读写线程使用 `poll` / `epoll` 监听事件。
 * 支持 `recvmsg` 、`sendmsg` 、`recv` 、`send` 、`recvfrom`、`sendto`、`getpeername` 、`getsockopt` 、`epoll_ctl` 等 posix 接口。
 * 网络通讯报文采用问答方式，丢包或者内容错误则报错并停止通讯。报文内容有变化，长度可配。
+* 支持网络故障注入，延迟进行（delay）、跳过（skip）read、write、accept等逻辑。
 
 ## 网络模型
 
@@ -103,15 +104,15 @@
 * `-a, --as [server | client]`：作为服务端还是客户端。
   * `server`：作为服务端。
   * `client`：作为客户端。
-* `-i, --ip [xxx.xxx.xxx.xxx]`：IP地址。
-* `-g, --groupip [xxx.xxx.xxx.xxx]`：UDP组播地址。
+* `-i, --ip [xxx.xxx.xxx.xxx]`：server端IP地址。当v4与v6地址同时存在时，以","分隔。例如：`-i 192.168.1.88,aa22:bb11:1122:cdef:1234:aa99:7654:7410`
+* `-g, --groupip [xxx.xxx.xxx.xxx,xxx.xxx.xxx.xxx]`：配置UDP组播地址与interface地址，以','分隔，其中interface地址为可选项。例如：`-g 224.0.0.24,192.168.1.202`或`-g 224.0.0.24`
 * `-p, --port [xxxx]`：端口。
 * `-m, --model [mum | mud]`：采用的网络模型类型。
   * `mum (multi thread, unblock, multiplexing IO)`：多线程非阻塞IO复用。
   * `mud (multi thread, unblock, dissymmetric)`：多线程非阻塞非对称。
 * `-t, --threadnum`：线程数设置。
 * `-c, --connectnum`：连接数设置。当 `domain` 设置为 `udp` 时，`connectnum` 会被设置为1。
-* `-D, --domain [unix | tcp | udp]`：通信协议。
+* `-D, --domain [unix | tcp | udp]`：通信协议。当支持多个通信协议时以","分隔。例如：`-D tcp,udp`
   * `unix`：基于 unix 协议实现。
   * `tcp`：基于 tcp 协议实现。
   * `udp`：基于 udp 协议实现。
@@ -132,6 +133,19 @@
 * `-C, --accept`：accept的方式。
   * `ac`：使用accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)通过套接口接受连接。
   * `ac4`：使用accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags)通过套接口接受连接,flags=SOCK_CLOEXEC。
+* `-k, --keep_alive`：配置TCP keep_alive idle ,  keep_alive interval时间(second)。
+* `-I, --inject`: 配置故障注入类型。
+  * `delay`:
+    * `"delay 20 before_accept"`: 延迟20秒进行accept，时间可自定义，需大于0。可用于构造tcp_acceptmbox_full的情景
+    * `"delay 20 before_read"`: 延迟20秒进行read，时间可自定义，需大于0。
+    * `"delay 20 before_write"`: 延迟20秒进行write，时间可自定义，需大于0。
+    * `"delay 20 before_read_and_write"`: 延迟20秒进行read和write，时间可自定义，需大于0。
+  * `skip`:
+    * `"skip write"`: 跳过写过程，并关闭链接。
+    * `"skip read"`: 跳过读过程，并关闭链接。
+    * `"skip read_and_write"`: 跳过读写写过程，并关闭链接。
+    
+
 ## 使用
 
  * **环境配置**
@@ -235,12 +249,12 @@ make
  * 创建udp组播服务端
 
 ```
-./example -A server -D udp -i 192.168.0.1 -g 225.0.0.1 -A recvfromsendto
+./example -A server -D udp -g 225.0.0.1,192.168.0.1 -A recvfromsendto
 
 [program parameters]: 
 --> [as]:                       server 
---> [server ip]:                192.168.0.1
 --> [server group ip]:          225.0.0.1
+--> [server groupip_interface]: 192.168.0.1
 --> [server port]:              5050 
 --> [model]:                    mum 
 --> [thread number]:            1 
@@ -260,12 +274,12 @@ make
  * 创建udp组播客户端
 
 ```
-./example -A client -D udp -i 192.168.0.1 -g 225.0.0.1 -A recvfromsendto
+./example -A client -D udp -g 225.0.0.1,192.168.0.1 -A recvfromsendto
 
 [program parameters]: 
---> [as]:                       server 
---> [server ip]:                225.0.0.1
---> [client send ip]:           192.168.0.1
+--> [as]:                       client 
+--> [client group ip]:          225.0.0.1
+--> [client groupip_interface]: 192.168.0.1
 --> [server port]:              5050 
 --> [thread number]:            1
 --> [connection number]:        1 
@@ -280,3 +294,50 @@ make
 [program informations]: 
 --> <client>: [connect num]: 0, [send]: 0.000 B/s
 ```
+
+* 混杂模式下server 与 client 配置
+```
+./example -a server -D tcp,udp -i 192.168.1.88 -p 33333 -g 224.0.0.24,192.168.1.188
+[program parameters]: 
+--> [as]:                       server 
+--> [server group ip]:          224.0.0.24 
+--> [server groupip_interface]: 192.168.1.188 
+--> [server ip]:                192.168.1.888 
+--> [server port]:              33333 
+--> [model]:                    mum 
+--> [thread number]:            1 
+--> [domain]:                   tcp,udp 
+--> [api]:                      read & write 
+--> [packet length]:            1024 
+--> [verify]:                   off 
+--> [ringpmd]:                  off 
+--> [debug]:                    off 
+--> [epoll create]:             ec 
+--> [accept]:                   ac 
+--> [inject]:                   none 
+
+[program informations]:
+```
+```
+./example -a client -D tcp,udp -i 192.168.1.188 -p 33333 -g 192.168.1.202,224.0.0.24
+[program parameters]: 
+--> [as]:                       client 
+--> [client group ip]:          224.0.0.24 
+--> [client groupip_interface]: 192.168.1.202 
+--> [server ip]:                192.168.1.188 
+--> [server port]:              33333 
+--> [thread number]:            1 
+--> [connection number]:        1 
+--> [domain]:                   tcp,udp 
+--> [api]:                      read & write 
+--> [packet length]:            1024 
+--> [verify]:                   off 
+--> [ringpmd]:                  off 
+--> [debug]:                    off 
+--> [epoll create]:             ec 
+--> [accept]:                   ac 
+--> [inject]:                   none 
+
+[program informations]: 
+
+```
\ No newline at end of file
diff --git a/examples/inc/bussiness.h b/examples/inc/bussiness.h
index 83645ef..3a78b1f 100644
--- a/examples/inc/bussiness.h
+++ b/examples/inc/bussiness.h
@@ -28,7 +28,9 @@
  */
 struct ServerHandler
 {
+    int32_t listen_fd_array[PROTOCOL_MODE_MAX];
     int32_t fd;                 ///< socket file descriptor
+    int32_t is_v6;
 };
 
 /**
@@ -39,6 +41,7 @@ struct ClientHandler
 {
     int32_t fd;                 ///< socket file descriptor
     uint32_t msg_idx;           ///< the start charactors index of message
+    int32_t sendtime_interverl; ///< udp send packet interverl
 };
 
 
@@ -90,24 +93,21 @@ int32_t client_bussiness(char *out, const char *in, uint32_t size, bool verify,
 /**
  * @brief server checks the information and answers
  * This function checks the information and answers.
- * @param server_handler    server handler
+ * @param fd                socket_fd
  * @param pktlen            the length of package
  * @param api               the api
  * @return                  the result
  */
-int32_t server_ans(struct ServerHandler *server_handler, uint32_t pktlen, const char* api, const char* domain);
+int32_t server_ans(int32_t fd, uint32_t pktlen, const char* api, const char* domain);
 
 /**
  * @brief client asks server
  * This function asks server.
  * @param client_handler    client handler
- * @param pktlen            the length of package
- * @param api               the api
- * @param domain            the domain
+ * @param client_unit       ClientUnit
  * @return                  the result
  */
-int32_t client_ask(struct ClientHandler *client_handler, uint32_t pktlen, const char* api, const char* domain, in_addr_t ip, uint16_t port);
-
+int32_t client_ask(struct ClientHandler *client_handler, struct ClientUnit *client_unit);
 /**
  * @brief client checks the information and answers
  * This function checks the information and answers.
@@ -119,7 +119,7 @@ int32_t client_ask(struct ClientHandler *client_handler, uint32_t pktlen, const
  * @param ip                the ip address of peer, maybe group ip
  * @return                  the result
  */
-int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, bool verify, const char* api, const char* domain, in_addr_t ip);
+int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, bool verify, const char* api, const char* domain, ip_addr_t* ip);
 
 
 #endif // __EXAMPLES_BUSSINESS_H__
diff --git a/examples/inc/client.h b/examples/inc/client.h
index 97af33f..0fe07aa 100644
--- a/examples/inc/client.h
+++ b/examples/inc/client.h
@@ -19,31 +19,8 @@
 #include "parameter.h"
 #include "bussiness.h"
 
-
-/**
- * @brief client unit
- * The information of each thread of client.
- */
-struct ClientUnit
-{
-    struct ClientHandler *handlers;     ///< the handlers
-    int32_t epfd;                       ///< the connect epoll file descriptor
-    struct epoll_event *epevs;          ///< the epoll events
-    uint32_t curr_connect;              ///< current connection number
-    uint64_t send_bytes;                ///< total send bytes
-    in_addr_t ip;                       ///< server ip
-    in_addr_t groupip;                  ///< server groupip
-    uint16_t port;                      ///< server port
-    uint16_t sport;                     ///< client sport
-    uint32_t connect_num;               ///< total connection number
-    uint32_t pktlen;                    ///< the length of peckage
-    bool verify;                        ///< if we verify or not
-    char* domain;                       ///< the communication domain
-    char* api;                          ///< the type of api
-    bool debug;                         ///< if we print the debug information
-    char* epollcreate;                  ///< epoll_create method
-    struct ClientUnit *next;            ///< next pointer
-};
+#define TIME_SCAN_INTERVAL 1
+#define TIME_SEND_INTERVAL 1
 
 /**
  * @brief client
@@ -53,8 +30,14 @@ struct Client
 {
     struct ClientUnit *uints;           ///< the server mum unit
     bool debug;                         ///< if we print the debug information
+    uint32_t threadNum;
+    bool loop;                          ///< judge client info print while loop is open
 };
 
+struct Client_domain_ip {
+    char *domain;
+    uint8_t ip_family;
+};
 
 /**
  * @brief the single thread, client prints informations
@@ -66,7 +49,7 @@ struct Client
  * @param debug             if debug or not
  * @return                  the result pointer
  */
-void client_debug_print(const char *ch_str, const char *act_str, in_addr_t ip, uint16_t port, bool debug);
+void client_debug_print(const char *ch_str, const char *act_str, ip_addr_t *ip, uint16_t port, bool debug);
 
 /**
  * @brief the client prints informations
@@ -86,7 +69,7 @@ void client_info_print(struct Client *client);
  * @param domain            domain
  * @return                  the result pointer
  */
-int32_t client_thread_try_connect(struct ClientHandler *client_handler, int32_t epoll_fd, in_addr_t ip, in_addr_t groupip, uint16_t port, uint16_t sport, const char *domain, const char *api);
+int32_t client_thread_try_connect(struct ClientHandler *client_handler, struct ClientUnit *client_unit);
 
 /**
  * @brief the single thread, client retry to connect to server, register to epoll
@@ -122,4 +105,11 @@ void *client_s_create_and_run(void *arg);
 int32_t client_create_and_run(struct ProgramParams *params);
 
 
+/**
+ * @brief loop server info
+ * This function print loop mode server info.
+ */
+void loop_info_print();
+
+
 #endif // __EXAMPLES_CLIENT_H__
diff --git a/examples/inc/parameter.h b/examples/inc/parameter.h
index 93e3672..ff2f114 100644
--- a/examples/inc/parameter.h
+++ b/examples/inc/parameter.h
@@ -20,6 +20,8 @@
 
 #define PARAM_DEFAULT_AS            ("server")              ///< default type
 #define PARAM_DEFAULT_IP            ("127.0.0.1")           ///< default IP
+#define PARAM_DEFAULT_IP_V6         ("0.0.0.0.0.0.0.0")           ///< default IP
+#define PARAM_DEFAULT_ADDR_FAMILY   (AF_INET)               ///< default address family
 #define PARAM_DEFAULT_PORT          (5050)                  ///< default port
 #define PARAM_DEFAULT_SPORT         (0)                     ///< default sport
 #define PARAM_DEFAULT_MODEL         ("mum")                 ///< default model type
@@ -34,6 +36,9 @@
 #define PARAM_DEFAULT_EPOLLCREATE   ("ec")                  ///< default method of epoll_create
 #define PARAM_DEFAULT_ACCEPT        ("ac")                  ///< default method of accept method
 #define PARAM_DEFAULT_GROUPIP       ("0.0.0.0")             ///< default group IP>
+#define PARAM_DEFAULT_KEEPALIVEIDLE (0)                     ///< default TCP_KEEPALIVE_IDLE_TIME>
+
+#define TCP_KEEPALIVE_IDLE_MAX (3600)                       // time: second
 
 
 enum {
@@ -43,7 +48,7 @@ enum {
     PARAM_NUM_IP = 'i',
 #define PARAM_NAME_PORT             ("port")                ///< name of parameter port
     PARAM_NUM_PORT = 'p',
-#define PARAM_NAME_SPORT            ("sport")                ///< name of parameter sport
+#define PARAM_NAME_SPORT            ("sport")               ///< name of parameter sport
     PARAM_NUM_SPORT = 's',
 #define PARAM_NAME_MODEL            ("model")               ///< name of parameter model type
     PARAM_NUM_MODEL = 'm',
@@ -71,12 +76,27 @@ enum {
     PARAM_NUM_ACCEPT = 'C',
 #define PARAM_NAME_GROUPIP          ("groupip")             ///< name of parameter group ip
     PARAM_NUM_GROUPIP = 'g',
+#define PARAM_NAME_KEEPALIVE        ("keep_alive")          ///< name of parameter keep_alive
+    PARAM_NUM_KEEPALIVE = 'k',
+#define PARAM_NAME_INJECT           ("inject")              ///< name of parameter fault inject
+    PARAM_NUM_INJECT = 'I',
 };
 
 #define NO_ARGUMENT             0                           ///< options takes no arguments
 #define REQUIRED_ARGUMETN       1                           ///< options requires arguments
 #define OPTIONAL_ARGUMETN       2                           ///< options arguments are optional
 
+uint8_t getbit_num(uint8_t mode, uint8_t index);
+uint8_t setbitnum_on(uint8_t mode, uint8_t index);
+uint8_t setbitnum_off(uint8_t mode, uint8_t index);
+
+uint8_t program_get_protocol_mode_by_domain_ip(char* domain, char* ipv4, char* ipv6, char* group_ip);
+
+struct ServerBaseCfgInfo {
+    const char *domain;
+    const char *api;
+    uint32_t pktlen;
+};
 
 /**
  * @brief program option description
@@ -96,12 +116,13 @@ struct ProgramOption {
 struct ProgramParams {
     char*               as;                 ///< as server or client
     char*               ip;                 ///< IP address
-    uint32_t            port;               ///< port
-    uint32_t            sport;               ///< sport
+    char*               ipv6;
+    bool                port[UNIX_TCP_PORT_MAX];       ///< index:port list; value:port is set or not
+    bool                sport[UNIX_TCP_PORT_MAX];       ///< index:sport list; value:sport is set or not
     char*               model;              ///< model type
     uint32_t            thread_num;         ///< the number of threads
     uint32_t            connect_num;        ///< the connection number
-    char*               domain;             ///< the communication dimain
+    char*               domain;             ///< the communication domain
     char*               api;                ///< the type of api
     uint32_t            pktlen;             ///< the packet length
     bool                verify;             ///< if we verify the message or not
@@ -110,8 +131,58 @@ struct ProgramParams {
     char*               accept;             ///< accept connections method
     bool                ringpmd;            ///< if we use ring PMD or not
     char*               groupip;            ///< group IP address>
+    char*               groupip_interface;  ///< udp multicast interface address>
+    uint32_t            addr_family;        ///< IP address family
+    int32_t             tcp_keepalive_idle; ///< tcp keepalive idle time
+    int32_t             tcp_keepalive_interval;  ///< tcp keepalive interval time
+#define INJECT_TYPE_IDX                     (0)                 ///< the index of inject type
+#define INJECT_TIME_IDX                     (1)                 ///< the index of delay time
+#define INJECT_SKIP_IDX                     (1)                 ///< the index of skip location
+#define INJECT_LOCATION_IDX                 (2)                 ///< the index of delay location
+#define FAULT_INJECT_PARA_COUNT             (3)                 ///< the count of fault injection parameters
+    char*               inject[FAULT_INJECT_PARA_COUNT];  /// < fault inject
 };
 
+typedef enum {
+    INJECT_DELAY_ACCEPT = 0,
+    INJECT_DELAY_READ,
+    INJECT_DELAY_WRITE,
+    INJECT_DELAY_MAX,
+}delay_type;
+
+typedef enum {
+    INJECT_SKIP_READ = 0,
+    INJECT_SKIP_WRITE,
+    INJECT_SKIP_MAX,
+} skip_type;
+
+typedef enum {
+    V4_TCP,
+    V6_TCP,
+    V4_UDP,
+    V6_UDP,
+    UDP_MULTICAST,
+    UNIX,
+    PROTOCOL_MODE_MAX
+} PROTOCOL_MODE_ENUM_TYPE;
+
+#define FAULT_INJECT_SKIP_BEGIN(skip_type)    \
+    if (get_g_inject_skip((skip_type))) {}    \
+    else {
+#define FAULT_INJECT_SKIP_END  }
+
+/**
+ * @brief return g_inject_skip value
+ * This function return g_inject_skip value to deside if excute skip
+ */
+int32_t get_g_inject_skip(skip_type type);
+
+/**
+ * @brief function execute delay inject
+ * This function delay execute following program.
+ */
+void fault_inject_delay(delay_type type);
+
 /**
  * @brief initialize the parameters
  * This function initializes the parameters of main function.
@@ -142,5 +213,6 @@ int32_t program_params_parse(struct ProgramParams *params, uint32_t argc, char *
  */
 void program_params_print(struct ProgramParams *params);
 
+bool ip_is_v6(const char *ip);
 
 #endif // __EXAMPLES_PARAMETER_H__
diff --git a/examples/inc/server.h b/examples/inc/server.h
index a3affef..4631a28 100644
--- a/examples/inc/server.h
+++ b/examples/inc/server.h
@@ -31,8 +31,7 @@ struct ServerMumUnit
     struct epoll_event *epevs;              ///< the epoll events
     uint32_t curr_connect;                  ///< current connection number
     uint64_t recv_bytes;                    ///< total receive bytes
-    in_addr_t ip;                           ///< server ip
-    in_addr_t groupip;                      ///< server group ip
+    struct ServerIpInfo server_ip_info;
     uint16_t port;                          ///< server port
     uint32_t pktlen;                        ///< the length of peckage
     char* domain;                           ///< communication domain
@@ -40,6 +39,9 @@ struct ServerMumUnit
     bool debug;                             ///< if we print the debug information
     char* epollcreate;                      ///< epoll_create method
     char* accept;                           ///< accept connections method
+    int32_t tcp_keepalive_idle;             ///< tcp keepalive idle time
+    int32_t tcp_keepalive_interval;         ///< tcp keepalive interval time
+    uint8_t protocol_type_mode;             ///< tcp/udp ipv4/ipv6 protocol mode
     struct ServerMumUnit *next;             ///< next pointer
 };
 
@@ -64,11 +66,13 @@ struct ServerMudWorker
     struct epoll_event *epevs;              ///< the epoll events
     uint64_t recv_bytes;                    ///< total receive bytes
     uint32_t pktlen;                        ///< the length of peckage
-    in_addr_t ip;                           ///< client ip
+    ip_addr_t ip;                           ///< client ip
     uint16_t port;                          ///< client port
     char* api;                              ///< the type of api
     bool debug;                             ///< if we print the debug information
     char* epollcreate;                      ///< epoll_create method
+    char* domain;
+    uint32_t curr_connect;
     struct ServerMudWorker *next;           ///< next pointer
 };
 
@@ -82,16 +86,17 @@ struct ServerMud
     struct ServerMudWorker *workers;        ///< the workers
     int32_t epfd;                           ///< the listen epoll file descriptor
     struct epoll_event *epevs;              ///< the epoll events
-    uint32_t curr_connect;                  ///< current connection number
-    in_addr_t ip;                           ///< server ip
-    in_addr_t groupip;                      ///< server group ip
-    uint16_t port;                          ///< server port
+    struct ServerIpInfo server_ip_info;
+    bool* port;                             ///< server port point to parameter's port
     uint32_t pktlen;                        ///< the length of peckage
     char* domain;                           ///< communication domain
     char* api;                              ///< the type of api
     bool debug;                             ///< if we print the debug information
     char* accept;                           ///< accept connections method
     char* epollcreate;                      ///< epoll_create method
+    int32_t tcp_keepalive_idle;             ///< tcp keepalive idle time
+    int32_t tcp_keepalive_interval;         ///< tcp keepalive interval time
+    uint8_t protocol_type_mode;             ///< tcp/udp ipv4/ipv6 protocol mode
 };
 
 
@@ -105,7 +110,7 @@ struct ServerMud
  * @param debug             if debug or not
  * @return                  the result pointer
  */
-void server_debug_print(const char *ch_str, const char *act_str, in_addr_t ip, uint16_t port, bool debug);
+void server_debug_print(const char *ch_str, const char *act_str, ip_addr_t *ip, uint16_t port, bool debug);
 
 /**
  * @brief the multi thread, unblock, dissymmetric server prints informations
@@ -136,7 +141,7 @@ int32_t sermud_listener_create_epfd_and_reg(struct ServerMud *server_mud);
  * @param server_mud        the server unit
  * @return                  the result pointer
  */
-int32_t sermud_listener_accept_connects(struct ServerMud *server_mud);
+int32_t sermud_listener_accept_connects(struct epoll_event *curr_epev, struct ServerMud *server_mud);
 
 /**
  * @brief the worker thread, unblock, dissymmetric server processes the events
@@ -200,7 +205,7 @@ int32_t sersum_create_epfd_and_reg(struct ServerMumUnit *server_unit);
  * @param server_handler    the server handler
  * @return                  the result pointer
  */
-int32_t sersum_accept_connects(struct ServerMumUnit *server_unit, struct ServerHandler *server_handler);
+int32_t sersum_accept_connects(struct epoll_event *cur_epev, struct ServerMumUnit *server_unit);
 
 /**
  * @brief the single thread, unblock, mutliplexing IO server processes the events
diff --git a/examples/inc/utilities.h b/examples/inc/utilities.h
index 0f9db4e..262481a 100644
--- a/examples/inc/utilities.h
+++ b/examples/inc/utilities.h
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <signal.h>
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -36,6 +37,7 @@
 #include <sys/un.h>
 
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
 
 #include "securec.h"
@@ -47,7 +49,7 @@
                                             { \
                                                 printf("\n[error]: "); \
                                                 printf(format, ##__VA_ARGS__); \
-                                                printf("\n"); \
+                                                printf("\n\n"); \
                                             } while (0)
 #define PRINT_WARNNING(format, ...)         do \
                                             { \
@@ -76,7 +78,7 @@
                                             } while(0)
 #define PRINT_CLIENT_DATAFLOW(format, ...)  do \
                                             { \
-                                                printf("\033[?25l\033[A\033[K"); \
+                                                printf(" "); \
                                                 printf("--> <client>: "); \
                                                 printf(format, ##__VA_ARGS__); \
                                                 printf("\033[?25h\n"); \
@@ -90,24 +92,94 @@
 #define PROGRAM_INPROGRESS                  (-2)                ///< program in progress flag
 
 #define UNIX_TCP_PORT_MIN                   (1024)              ///< TCP minimum port number in unix
-#define UNIX_TCP_PORT_MAX                   (65535)             ///< TCP minimum port number in unix
+#define UNIX_TCP_PORT_MAX                   (65535)             ///< TCP maximum port number in unix
 #define THREAD_NUM_MIN                      (1)                 ///< minimum number of thead
 #define THREAD_NUM_MAX                      (1000)              ///< maximum number of thead
 #define MESSAGE_PKTLEN_MIN                  (2)                 ///< minimum length of message (1 byte)
 #define MESSAGE_PKTLEN_MAX                  (1024 * 1024 * 10)  ///< maximum length of message (10 Mb)
+#define UDP_PKTLEN_MAX                      (65507)             ///< maximum length of udp message
 
-#define SERVER_SOCKET_LISTEN_BACKLOG        (128)               ///< the queue of socket
+#define SERVER_SOCKET_LISTEN_BACKLOG        (4096)              ///< the queue of socket
 #define SERVER_EPOLL_SIZE_MAX               (10000)             ///< the max wait event of epoll
 #define SERVER_EPOLL_WAIT_TIMEOUT           (-1)                ///< the timeout value of epoll
 
 #define CLIENT_EPOLL_SIZE_MAX               (10000)             ///< the max wait event of epoll
 #define CLIENT_EPOLL_WAIT_TIMEOUT           (-1)                ///< the timeout value of epoll
 
-#define TERMINAL_REFRESH_MS                 (100)               ///< the time cut off between of terminal refresh
+#define TERMINAL_REFRESH_MS                 (500)               ///< the time cut off between of terminal refresh
 
 #define SOCKET_UNIX_DOMAIN_FILE             "unix_domain_file"  ///< socket unix domain file
 
+#define IPV4_STR "V4"
+#define IPV6_STR "V6"
+#define IPV4_MULTICAST "Multicast"
+#define INVAILD_STR "STR_NULL"
+
+#define TIMES_CONVERSION_RATE (1000)
+#define KB (1024)
+#define MB (KB * KB)
+#define GB (MB * MB)
+
+struct ThreadUintInfo {
+    uint64_t send_bytes;                ///< total send bytes
+    uint32_t cur_connect_num;           ///< total connection number
+    char* domain;
+    char* ip_type_info;
+    pthread_t thread_id;
+};
+
+typedef struct ip_addr {
+    struct {
+        struct in_addr ip4;
+        struct in6_addr ip6;
+    } u_addr;
+    uint32_t addr_family;
+} ip_addr_t;
+
+typedef union sockaddr_union {
+    struct sockaddr     sa;
+    struct sockaddr_in  in;
+    struct sockaddr_in6 in6;
+} sockaddr_t;
 
+/**
+ * @brief client unit
+ * The information of each thread of client.
+ */
+struct ClientUnit {
+    struct ClientHandler *handlers;     ///< the handlers
+    int32_t epfd;                       ///< the connect epoll file descriptor
+    struct epoll_event *epevs;          ///< the epoll events
+    uint32_t curr_connect;              ///< current connection number
+    ip_addr_t ip;                       ///< server ip
+    ip_addr_t groupip;                  ///< server groupip
+    uint32_t port;                      ///< server port
+    ip_addr_t groupip_interface;        ///< udp multicast interface address>
+    uint32_t sport;                     ///< client sport
+    uint32_t connect_num;               ///< total connection number
+    uint32_t pktlen;                    ///< the length of peckage
+    uint32_t loop;                      ///< the packet send to loop
+    bool verify;                        ///< if we verify or not
+    char* domain;                       ///< the communication domain
+    char* api;                          ///< the type of api
+    bool debug;                         ///< if we print the debug information
+    char* epollcreate;                  ///< epoll_create method
+    uint8_t protocol_type_mode;         ///< tcp/udp ipv4/ipv6 protocol mode
+    struct ThreadUintInfo threadVolume;
+    struct ClientUnit *next;            ///< next pointer
+};
+struct ServerIpInfo {
+    ip_addr_t ip;                           ///< server ip
+    ip_addr_t groupip;                      ///< server group ip
+    ip_addr_t groupip_interface;            ///< server group interface ip
+};
+
+struct LoopInfo {
+    char* model;
+    struct ServerMud *server_mud_info;
+    struct ServerMum *server_mum_info;
+};
+extern struct LoopInfo loopmod;
 /**
  * @brief create the socket and listen
  * Thi function creates the socket and listen.
@@ -118,7 +190,8 @@
  * @param domain        domain
  * @return              the result
  */
-int32_t create_socket_and_listen(int32_t *socket_fd, in_addr_t ip, in_addr_t groupip, uint16_t port, const char *domain);
+int32_t create_socket_and_listen(int32_t *listen_fd_array, struct ServerIpInfo *server_ip_info, uint16_t port,
+                                 uint8_t protocol_mode);
 
 /**
  * @brief create the socket and connect
@@ -131,7 +204,7 @@ int32_t create_socket_and_listen(int32_t *socket_fd, in_addr_t ip, in_addr_t gro
  * @param api           api
  * @return              the result
  */
-int32_t create_socket_and_connect(int32_t *socket_fd, in_addr_t ip, in_addr_t groupip, uint16_t port, uint16_t sport, const char *domain, const char *api);
+int32_t create_socket_and_connect(int32_t *socket_fd, struct ClientUnit *client_unit);
 
 /**
  * @brief set the socket to unblock
@@ -140,6 +213,7 @@ int32_t create_socket_and_connect(int32_t *socket_fd, in_addr_t ip, in_addr_t gr
  * @return              the result
  */
 int32_t set_socket_unblock(int32_t socket_fd);
+int32_t set_tcp_keep_alive_info(int32_t sockfd, int32_t tcp_keepalive_idle, int32_t tcp_keepalive_interval);
 
 
 #endif // __EXAMPLES_UTILITIES_H__
diff --git a/examples/main.c b/examples/main.c
index 5338572..dfee2db 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -31,7 +31,12 @@ int32_t main(int argc, char *argv[])
 
     if (strcmp(prog_params.as, "server") == 0) {
         server_create_and_run(&prog_params);
-    } else {
+    } else if (strcmp(prog_params.as, "client") == 0) {
+        client_create_and_run(&prog_params);
+    } else if (strcmp(prog_params.as, "loop") == 0) {
+        server_create_and_run(&prog_params);
+        /* sleep to wait server creating */
+        sleep(1);
         client_create_and_run(&prog_params);
     }
 
diff --git a/examples/src/bussiness.c b/examples/src/bussiness.c
index 7263371..46c99fe 100644
--- a/examples/src/bussiness.c
+++ b/examples/src/bussiness.c
@@ -11,8 +11,9 @@
 */
 
 
-#include "bussiness.h"
+#include "parameter.h"
 #include "client.h"
+#include "bussiness.h"
 
 
 static const char bussiness_messages_low[] = "abcdefghijklmnopqrstuvwxyz";  // the lower charactors of business message
@@ -135,41 +136,41 @@ int32_t client_bussiness(char *out, const char *in, uint32_t size, bool verify,
     return PROGRAM_OK;
 }
 
-// server answers
-int32_t server_ans(struct ServerHandler *server_handler, uint32_t pktlen, const char* api, const char* domain)
+static void server_ans_free_buff(char *buff_in, char *buff_out)
 {
-    const uint32_t length = pktlen;
-    char *buffer_in = (char *)malloc(length * sizeof(char));
-    char *buffer_out = (char *)malloc(length * sizeof(char));
+    if (buff_in) {
+        free(buff_in);
+    }
+    if (buff_out) {
+        free(buff_out);
+    }
+}
+
+// server_ans_read
+static int32_t server_ans_read(int32_t socket_fd, struct ServerBaseCfgInfo *server_base_info, char *buffer_in,
+                               struct sockaddr *client_addr)
+{
+    const uint32_t length = server_base_info->pktlen;
+    const char *api = server_base_info->api;
+    const char *domain = server_base_info->domain;
 
     int32_t cread = 0;
     int32_t sread = length;
     int32_t nread = 0;
-    struct sockaddr_in client_addr;
-    socklen_t len = sizeof(client_addr);
 
-    if (strcmp(domain, "udp") == 0 && strncmp(api, "recvfrom", strlen("recvfrom")) != 0) {
-        if (getpeername(server_handler->fd, (struct sockaddr *)&client_addr, &len) < 0) {
-            if (recvfrom(server_handler->fd, buffer_in, length, MSG_PEEK, (struct sockaddr *)&client_addr, &len) < 0) {
-                return PROGRAM_FAULT;
-            }
-            if (connect(server_handler->fd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in)) < 0) {
-                return PROGRAM_FAULT;
-            }
-        }
-    }
+    socklen_t len = sizeof(sockaddr_t);
 
     while (cread < sread) {
         if (strcmp(domain, "udp") == 0 && strcmp(api, "recvfromsendto") == 0) {
-            nread = recvfrom(server_handler->fd, buffer_in, length, 0, (struct sockaddr *)&client_addr, &len);
+            nread = recvfrom(socket_fd, buffer_in, length, 0, client_addr, &len);
         } else {
-            nread = read_api(server_handler->fd, buffer_in, length, api);
+            nread = read_api(socket_fd, buffer_in, length, api);
         }
-
         if (nread == 0) {
             return PROGRAM_ABORT;
         } else if (nread < 0) {
             if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
+                PRINT_ERROR("nread =%d, errno=%d", nread, errno);
                 return PROGRAM_FAULT;
             }
         } else {
@@ -177,66 +178,152 @@ int32_t server_ans(struct ServerHandler *server_handler, uint32_t pktlen, const
             continue;
         }
     }
+    return PROGRAM_OK;
+}
 
-    if (strcmp(api, "recvfrom") == 0) {
-        free(buffer_in);
-        free(buffer_out);
-        return PROGRAM_OK;
-    }
-
-    server_bussiness(buffer_out, buffer_in, length);
+static int32_t server_ans_write(int32_t socket_fd, struct ServerBaseCfgInfo *server_base_info, char *buffer_out,
+                                struct sockaddr *client_addr)
+{
+    const uint32_t length = server_base_info->pktlen;
+    const char *api = server_base_info->api;
+    const char *domain = server_base_info->domain;
 
     int32_t cwrite = 0;
     int32_t swrite = length;
     int32_t nwrite = 0;
+    socklen_t len = sizeof(sockaddr_t);
+
     while (cwrite < swrite) {
         if (strcmp(domain, "udp") == 0 && strcmp(api, "recvfromsendto") == 0) {
-            nwrite = sendto(server_handler->fd, buffer_out, length, 0, (struct sockaddr *)&client_addr, len);
+            nwrite = sendto(socket_fd, buffer_out, swrite - cwrite, 0, client_addr, len);
         } else {
-            nwrite = write_api(server_handler->fd, buffer_out, length, api);
+            nwrite = write_api(socket_fd, buffer_out, swrite - cwrite, api);
         }
 
         if (nwrite == 0) {
             return PROGRAM_ABORT;
         } else if (nwrite < 0) {
-             if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
+            if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
+                PRINT_ERROR("nwrite =%d, errno=%d", nwrite, errno);
                 return PROGRAM_FAULT;
-             }
+            }
         } else {
             cwrite += nwrite;
             continue;
         }
     }
+    return PROGRAM_OK;
+}
 
-    free(buffer_in);
-    free(buffer_out);
+// server answers
+int32_t server_ans(int32_t fd, uint32_t pktlen, const char* api, const char* domain)
+{
+    const uint32_t length = pktlen;
+    char *buffer_in = (char *)calloc(length, sizeof(char));
+    char *buffer_out = (char *)calloc(length, sizeof(char));
+    if (buffer_in == NULL || buffer_out == NULL) {
+        return PROGRAM_FAULT;
+    }
+
+    struct ServerBaseCfgInfo server_base_info;
+    server_base_info.domain = domain;
+    server_base_info.api = api;
+    server_base_info.pktlen = pktlen;
+
+    sockaddr_t client_addr;
+    socklen_t len = sizeof(sockaddr_t);
+
+    if (strcmp(domain, "udp") == 0 && strncmp(api, "recvfrom", strlen("recvfrom")) != 0) {
+        if (getpeername(fd, (struct sockaddr *)&client_addr, &len) < 0) {
+            if (recvfrom(fd, buffer_in, length, MSG_PEEK, (struct sockaddr *)&client_addr, &len) < 0) {
+                server_ans_free_buff(buffer_in, buffer_out);
+                return PROGRAM_FAULT;
+            }
+            if (connect(fd, (struct sockaddr *)&client_addr, len) < 0) {
+                server_ans_free_buff(buffer_in, buffer_out);
+                return PROGRAM_FAULT;
+            }
+        }
+    }
+
+    fault_inject_delay(INJECT_DELAY_READ);
+    FAULT_INJECT_SKIP_BEGIN(INJECT_SKIP_READ)
+
+    if (server_ans_read(fd, &server_base_info, buffer_in, (struct sockaddr *)&client_addr) != PROGRAM_OK) {
+        server_ans_free_buff(buffer_in, buffer_out);
+        return PROGRAM_FAULT;
+    }
+
+    FAULT_INJECT_SKIP_END
+    
+    if (strcmp(api, "recvfrom") == 0) {
+        server_ans_free_buff(buffer_in, buffer_out);
+        return PROGRAM_OK;
+    }
+
+    server_bussiness(buffer_out, buffer_in, length);
+    
+    fault_inject_delay(INJECT_DELAY_WRITE);
+    FAULT_INJECT_SKIP_BEGIN(INJECT_SKIP_WRITE)
+
+    if (server_ans_write(fd, &server_base_info, buffer_out, (struct sockaddr *)&client_addr) != PROGRAM_OK) {
+        server_ans_free_buff(buffer_in, buffer_out);
+        return PROGRAM_FAULT;
+    }
+
+    FAULT_INJECT_SKIP_END
+
+    server_ans_free_buff(buffer_in, buffer_out);
 
     return PROGRAM_OK;
 }
 
 // client asks
-int32_t client_ask(struct ClientHandler *client_handler, uint32_t pktlen, const char* api, const char* domain, in_addr_t ip, uint16_t port)
+int32_t client_ask(struct ClientHandler *client_handler, struct ClientUnit *client_unit)
 {
-    const uint32_t length = pktlen;
-    char *buffer_in = (char *)malloc(length * sizeof(char));
-    char *buffer_out = (char *)malloc(length * sizeof(char));
-    struct sockaddr_in server_addr;
-    socklen_t len = sizeof(server_addr);
-    memset_s(&server_addr, sizeof(server_addr), 0, sizeof(server_addr));
-    server_addr.sin_family = AF_INET;
-    server_addr.sin_addr.s_addr = ip;
-    server_addr.sin_port = port;
+    const char *api = client_unit->api;
+    const char *domain = client_unit->domain;
+
+    ip_addr_t *ip = client_unit->protocol_type_mode == UDP_MULTICAST ? &client_unit->groupip : &client_unit->ip;
+    uint16_t port = client_unit->port;
+
+    const uint32_t length = client_unit->pktlen;
+    char *buffer_in = (char *)calloc(length, sizeof(char));
+    char *buffer_out = (char *)calloc(length, sizeof(char));
+    if (buffer_in == NULL || buffer_out == NULL) {
+        return PROGRAM_FAULT;
+    }
+    sockaddr_t server_addr;
+    socklen_t len = 0;
+
+    if (ip->addr_family == AF_INET6) {
+        memset_s(&server_addr, sizeof(struct sockaddr_in6), 0, sizeof(struct sockaddr_in6));
+        ((struct sockaddr_in6 *)&server_addr)->sin6_family = AF_INET6;
+        ((struct sockaddr_in6 *)&server_addr)->sin6_addr = ip->u_addr.ip6;
+        ((struct sockaddr_in6 *)&server_addr)->sin6_port = port;
+        len = sizeof(struct sockaddr_in6);
+    } else if (ip->addr_family == AF_INET) {
+        memset_s(&server_addr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
+        ((struct sockaddr_in *)&server_addr)->sin_family = AF_INET;
+        ((struct sockaddr_in *)&server_addr)->sin_addr = ip->u_addr.ip4;
+        ((struct sockaddr_in *)&server_addr)->sin_port = port;
+        len = sizeof(struct sockaddr_in);
+    }
 
     client_bussiness(buffer_out, buffer_in, length, false, &(client_handler->msg_idx));
 
     int32_t cwrite = 0;
     int32_t swrite = length;
     int32_t nwrite = 0;
+
+    fault_inject_delay(INJECT_DELAY_WRITE);
+    FAULT_INJECT_SKIP_BEGIN(INJECT_SKIP_WRITE)
+    
     while (cwrite < swrite) {
         if (strcmp(domain, "udp") == 0 && strcmp(api, "recvfromsendto") == 0) {
-            nwrite = sendto(client_handler->fd, buffer_out, length, 0, (struct sockaddr *)&server_addr, len);
+            nwrite = sendto(client_handler->fd, buffer_out, swrite - cwrite, 0, (struct sockaddr *)&server_addr, len);
         } else {
-            nwrite = write_api(client_handler->fd, buffer_out, length, api);
+            nwrite = write_api(client_handler->fd, buffer_out, swrite - cwrite, api);
         }
         if (nwrite == 0) {
             return PROGRAM_ABORT;
@@ -250,6 +337,8 @@ int32_t client_ask(struct ClientHandler *client_handler, uint32_t pktlen, const
         }
     }
 
+    FAULT_INJECT_SKIP_END
+
     free(buffer_in);
     free(buffer_out);
 
@@ -257,18 +346,24 @@ int32_t client_ask(struct ClientHandler *client_handler, uint32_t pktlen, const
 }
 
 // client checks
-int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, bool verify, const char* api, const char* domain, in_addr_t ip)
+int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, bool verify, const char* api, const char* domain, ip_addr_t* ip)
 {
     const uint32_t length = pktlen;
-    char *buffer_in = (char *)malloc(length * sizeof(char));
-    char *buffer_out = (char *)malloc(length * sizeof(char));
+    char *buffer_in = (char *)calloc(length, sizeof(char));
+    char *buffer_out = (char *)calloc(length, sizeof(char));
+    if (buffer_in == NULL || buffer_out == NULL) {
+        return PROGRAM_FAULT;
+    }
 
     int32_t cread = 0;
     int32_t sread = length;
     int32_t nread = 0;
-    struct sockaddr_in server_addr;
-    socklen_t len = sizeof(server_addr);
+    sockaddr_t server_addr;
+    socklen_t len = ip->addr_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
 
+    fault_inject_delay(INJECT_DELAY_READ);
+    FAULT_INJECT_SKIP_BEGIN(INJECT_SKIP_READ)
+    
     while (cread < sread) {
         if (strcmp(domain, "udp") == 0 && strcmp(api, "recvfromsendto") == 0) {
             nread = recvfrom(client_handler->fd, buffer_in, length, 0, (struct sockaddr *)&server_addr, &len);
@@ -287,6 +382,8 @@ int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, boo
         }
     }
 
+    FAULT_INJECT_SKIP_END
+    
     if (client_bussiness(buffer_out, buffer_in, length, verify, &(client_handler->msg_idx)) < 0) {
         PRINT_ERROR("message verify fault! ");
         getchar();
@@ -295,15 +392,18 @@ int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, boo
     int32_t cwrite = 0;
     int32_t swrite = length;
     int32_t nwrite = 0;
-    if (ip >= inet_addr("224.0.0.0") && ip <= inet_addr("239.255.255.255")) {
-        server_addr.sin_addr.s_addr = ip;
+    if (ip->addr_family == AF_INET && ip->u_addr.ip4.s_addr >= inet_addr("224.0.0.0") && ip->u_addr.ip4.s_addr <= inet_addr("239.255.255.255")) {
+        ((struct sockaddr_in*)&server_addr)->sin_addr = ip->u_addr.ip4;
     }
 
+    fault_inject_delay(INJECT_DELAY_WRITE);
+    FAULT_INJECT_SKIP_BEGIN(INJECT_SKIP_WRITE)
+
     while (cwrite < swrite) {
         if (strcmp(domain, "udp") == 0 && strcmp(api, "recvfromsendto") == 0) {
-            nwrite = sendto(client_handler->fd, buffer_out, length, 0, (struct sockaddr *)&server_addr, len);
+            nwrite = sendto(client_handler->fd, buffer_out, swrite - cwrite, 0, (struct sockaddr *)&server_addr, len);
         } else {
-            nwrite = write_api(client_handler->fd, buffer_out, length, api);
+            nwrite = write_api(client_handler->fd, buffer_out, swrite - cwrite, api);
         }
         if (nwrite == 0) {
             return PROGRAM_ABORT;
@@ -317,6 +417,8 @@ int32_t client_chkans(struct ClientHandler *client_handler, uint32_t pktlen, boo
         }
     }
 
+    FAULT_INJECT_SKIP_END
+    
     free(buffer_in);
     free(buffer_out);
 
diff --git a/examples/src/client.c b/examples/src/client.c
index 1366924..43fbd0e 100644
--- a/examples/src/client.c
+++ b/examples/src/client.c
@@ -12,24 +12,61 @@
 
 
 #include "client.h"
-
+#include "server.h"
 
 static pthread_mutex_t client_debug_mutex;      // the client mutex for printf
+struct Client *g_client_begin = NULL;
+
+static int32_t client_process_ask(struct ClientHandler *client_handler, struct ClientUnit *client_unit);
+static void client_get_domain_ipversion(uint8_t protocol_type, struct ClientUnit *client_unit);
 
+static void timer_handle(int signum)
+{
+    if (g_client_begin == NULL) {
+        return;
+    }
+
+    struct ClientUnit *begin_client_unit = g_client_begin->uints;
+    while (begin_client_unit != NULL) {
+        if (begin_client_unit->domain != NULL && strcmp(begin_client_unit->domain, "udp") != 0) {
+            begin_client_unit = begin_client_unit->next;
+            continue;
+        }
+        for (int32_t i = 0; i < begin_client_unit->connect_num; i++) {
+            struct ClientHandler *handle = begin_client_unit->handlers + i;
+            if (handle->sendtime_interverl == TIME_SEND_INTERVAL) {
+                client_process_ask(handle, begin_client_unit);
+            } else {
+                handle->sendtime_interverl++;
+            }
+        }
+
+        begin_client_unit = begin_client_unit->next;
+    }
+    alarm(TIME_SCAN_INTERVAL);
+}
+
+static struct Client_domain_ip g_cfgmode_map[PROTOCOL_MODE_MAX] = {
+    [V4_TCP] = {"tcp", AF_INET},
+    [V6_TCP] = {"tcp", AF_INET6},
+    [V4_UDP] = {"udp", AF_INET},
+    [V6_UDP] = {"udp", AF_INET6},
+    [UDP_MULTICAST] = {"udp", AF_INET}};
 
 // the single thread, client prints informations
-void client_debug_print(const char *ch_str, const char *act_str, in_addr_t ip, uint16_t port, bool debug)
+void client_debug_print(const char *ch_str, const char *act_str, ip_addr_t *ip, uint16_t port, bool debug)
 {
     if (debug == true) {
         pthread_mutex_lock(&client_debug_mutex);
-        struct in_addr sin_addr;
-        sin_addr.s_addr = ip;
+        uint8_t str_len = ip->addr_family == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
+        char str_ip[str_len];
+        inet_ntop(ip->addr_family, &ip->u_addr, str_ip, str_len);
         PRINT_CLIENT("[%s] [pid: %d] [tid: %ld] [%s <- %s:%d]. ", \
                     ch_str, \
                     getpid(), \
                     pthread_self(), \
                     act_str, \
-                    inet_ntoa(sin_addr), \
+                    str_ip, \
                     ntohs(port));
         pthread_mutex_unlock(&client_debug_mutex);
     }
@@ -41,7 +78,8 @@ void client_info_print(struct Client *client)
     if (client->debug == false) {
         struct timeval begin;
         gettimeofday(&begin, NULL);
-        uint64_t begin_time = (uint64_t)begin.tv_sec * 1000 + (uint64_t)begin.tv_usec / 1000;
+        uint64_t begin_time = (uint64_t)begin.tv_sec * TIMES_CONVERSION_RATE +
+                              (uint64_t)begin.tv_usec / TIMES_CONVERSION_RATE;
 
         uint32_t curr_connect = 0;
         double bytes_ps = 0;
@@ -49,45 +87,164 @@ void client_info_print(struct Client *client)
         struct ClientUnit *begin_uint = client->uints;
         while (begin_uint != NULL) {
             curr_connect += begin_uint->curr_connect;
-            begin_send_bytes += begin_uint->send_bytes;
+            begin_send_bytes += begin_uint->threadVolume.send_bytes;
             begin_uint = begin_uint->next;
         }
 
         struct timeval delay;
         delay.tv_sec = 0;
-        delay.tv_usec = TERMINAL_REFRESH_MS * 1000;
+        delay.tv_usec = TERMINAL_REFRESH_MS * TIMES_CONVERSION_RATE;
         select(0, NULL, NULL, NULL, &delay);
 
         uint64_t end_send_bytes = 0;
         struct ClientUnit *end_uint = client->uints;
         while (end_uint != NULL) {
-            end_send_bytes += end_uint->send_bytes;
+            end_send_bytes += end_uint->threadVolume.send_bytes;
             end_uint = end_uint->next;
         }
 
         struct timeval end;
         gettimeofday(&end, NULL);
-        uint64_t end_time = (uint64_t)end.tv_sec * 1000 + (uint64_t)end.tv_usec / 1000;
-        
+        uint64_t end_time = (uint64_t)end.tv_sec * TIMES_CONVERSION_RATE +
+                            (uint64_t)end.tv_usec / TIMES_CONVERSION_RATE;
+
         double bytes_sub = end_send_bytes > begin_send_bytes ? (double)(end_send_bytes - begin_send_bytes) : 0;
-        double time_sub = end_time > begin_time ? (double)(end_time - begin_time) / 1000 : 0;
+        double time_sub = end_time > begin_time ? (double)(end_time - begin_time) / TIMES_CONVERSION_RATE : 0;
 
         bytes_ps = bytes_sub  / time_sub;
 
-        if (bytes_ps < 1024) {
+        if (bytes_ps < KB) {
             PRINT_CLIENT_DATAFLOW("[connect num]: %d, [send]: %.3f B/s", curr_connect, bytes_ps);
-        } else if (bytes_ps < (1024 * 1024)) {
-            PRINT_CLIENT_DATAFLOW("[connect num]: %d, [send]: %.3f KB/s", curr_connect, bytes_ps / 1024);
+        } else if (bytes_ps < MB) {
+            PRINT_CLIENT_DATAFLOW("[connect num]: %d, [send]: %.3f KB/s", curr_connect, bytes_ps / KB);
         } else {
-            PRINT_CLIENT_DATAFLOW("[connect num]: %d, [send]: %.3f MB/s", curr_connect, bytes_ps / (1024 * 1024));
+            PRINT_CLIENT_DATAFLOW("[connect num]: %d, [send]: %.3f MB/s", curr_connect, bytes_ps / MB);
+        }
+        
+        if (client->loop) {
+            printf("\033[2A\033[120C\033[K\n");
+            return;
+        }
+        printf("\033[A\033[K");
+    }
+}
+
+static int32_t client_process_ask(struct ClientHandler *client_handler, struct ClientUnit *client_unit)
+{
+    // not support udp+v6 currently
+    if (strcmp(client_unit->domain, "udp") == 0 && client_unit->ip.addr_family == AF_INET6) {
+        return PROGRAM_OK;
+    }
+
+    int32_t client_ask_ret = client_ask(client_handler, client_unit);
+    if (client_ask_ret == PROGRAM_FAULT) {
+        --client_unit->curr_connect;
+        struct epoll_event ep_ev;
+        if (client_handler->fd > 0 && epoll_ctl(client_unit->epfd, EPOLL_CTL_DEL, (client_handler)->fd, &ep_ev) < 0) {
+            PRINT_ERROR("client can't delete socket '%d' to control epoll %d! ", (client_handler)->fd, errno);
+            return PROGRAM_FAULT;
         }
+    } else if (client_ask_ret == PROGRAM_ABORT) {
+        --client_unit->curr_connect;
+        if (close((client_handler)->fd) < 0) {
+            PRINT_ERROR("client can't close the socket! ");
+            return PROGRAM_FAULT;
+        }
+        client_debug_print("client unit", "close", &client_unit->ip, client_unit->port, client_unit->debug);
+    } else {
+        client_unit->threadVolume.send_bytes += client_unit->pktlen;
+        client_handler->sendtime_interverl = 0;
+        client_debug_print("client unit", "send", &client_unit->ip, client_unit->port, client_unit->debug);
+    }
+    return PROGRAM_OK;
+}
+
+static void client_get_thread_volume(struct Client *client, struct ThreadUintInfo *threadVolume)
+{
+    int index = 0;
+    struct ClientUnit *curUint = client->uints;
+    while (curUint != NULL && index < client->threadNum) {
+        threadVolume[index].send_bytes = curUint->threadVolume.send_bytes;
+
+        threadVolume[index].cur_connect_num = curUint->curr_connect;
+        threadVolume[index].thread_id = curUint->threadVolume.thread_id;
+        threadVolume[index].domain = curUint->threadVolume.domain;
+        threadVolume[index].ip_type_info = curUint->threadVolume.ip_type_info;
+        curUint = curUint->next;
+        index++;
+    }
+}
+
+void client_info_print_mixed(struct Client *client, struct ThreadUintInfo *threadVolume,
+                             struct ThreadUintInfo *endThreadVolume)
+{
+    if (client->debug == true) {
+        return;
+    }
+    int32_t pthread_num = client->threadNum;
+    int32_t not_support_thread = 0;
+    struct timeval cur = {0};
+
+    gettimeofday(&cur, NULL);
+    uint64_t begin_time = (uint64_t)cur.tv_sec * TIMES_CONVERSION_RATE + (uint64_t)cur.tv_usec / TIMES_CONVERSION_RATE;
+
+    client_get_thread_volume(client, threadVolume);
+
+    struct timeval delay;
+    delay.tv_sec = 0;
+    delay.tv_usec = TERMINAL_REFRESH_MS * TIMES_CONVERSION_RATE;
+    select(0, NULL, NULL, NULL, &delay);
+
+    client_get_thread_volume(client, endThreadVolume);
+
+    gettimeofday(&cur, NULL);
+    uint64_t end_time = (uint64_t)cur.tv_sec * TIMES_CONVERSION_RATE + (uint64_t)cur.tv_usec / TIMES_CONVERSION_RATE;
+
+    for (int i = 0; i < pthread_num; i++) {
+        uint64_t begin_send_bytes = threadVolume[i].send_bytes;
+        uint64_t end_send_bytes = endThreadVolume[i].send_bytes;
+        pthread_t thread_id = endThreadVolume[i].thread_id;
+        uint32_t connect_num = endThreadVolume[i].cur_connect_num;
+        char *domain = endThreadVolume[i].domain;
+        char *ip_ver = endThreadVolume[i].ip_type_info;
+
+        if (thread_id == 0) {
+            not_support_thread++;
+            continue;
+        }
+
+        double bytes_sub = end_send_bytes > begin_send_bytes ? (double)(end_send_bytes - begin_send_bytes) : 0;
+        double time_sub = end_time > begin_time ? (double)(end_time - begin_time) / TIMES_CONVERSION_RATE : 0;
+        double bytes_ps = bytes_sub / time_sub;
+
+        if (bytes_ps < KB) {
+            PRINT_CLIENT_DATAFLOW("threadID=%-15lu, %s_%-9s [connect num]: %u, [send]: %.3f B/s",
+                                  thread_id, domain, ip_ver, connect_num, bytes_ps);
+        } else if (bytes_ps < MB) {
+            PRINT_CLIENT_DATAFLOW("threadID=%-15lu, %s_%-9s [connect num]: %u, [send]: %.3f kB/s",
+                                  thread_id, domain, ip_ver, connect_num, bytes_ps / KB);
+        } else {
+            PRINT_CLIENT_DATAFLOW("threadID=%-15lu, %s_%-9s [connect num]: %u, [send]: %.3f MB/s",
+                                  thread_id, domain, ip_ver, connect_num, bytes_ps / MB);
+        }
+    }
+    printf("\033[%dA\033[K", pthread_num - not_support_thread);
+}
+
+void loop_info_print()
+{
+    printf(" ");
+    if (strcmp(loopmod.model, "mum") == 0) {
+        sermum_info_print(loopmod.server_mum_info);
+    } else {
+        sermud_info_print(loopmod.server_mud_info);
     }
 }
 
 // the single thread, client try to connect to server, register to epoll
-int32_t client_thread_try_connect(struct ClientHandler *client_handler, int32_t epoll_fd, in_addr_t ip, in_addr_t groupip, uint16_t port, uint16_t sport, const char *domain, const char *api)
+int32_t client_thread_try_connect(struct ClientHandler *client_handler, struct ClientUnit *client_unit)
 {
-    int32_t create_socket_and_connect_ret = create_socket_and_connect(&(client_handler->fd), ip, groupip, port, sport, domain, api);
+    int32_t create_socket_and_connect_ret = create_socket_and_connect(&(client_handler->fd), client_unit);
     if (create_socket_and_connect_ret == PROGRAM_INPROGRESS) {
         return PROGRAM_OK;
     }
@@ -97,7 +254,7 @@ int32_t client_thread_try_connect(struct ClientHandler *client_handler, int32_t
 // the single thread, client retry to connect to server, register to epoll
 int32_t client_thread_retry_connect(struct ClientUnit *client_unit, struct ClientHandler *client_handler)
 {
-    int32_t clithd_try_cnntask_ret = client_thread_try_connect(client_handler, client_unit->epfd, client_unit->ip, client_unit->groupip, client_unit->port, client_unit->sport, client_unit->domain, client_unit->api);
+    int32_t clithd_try_cnntask_ret = client_thread_try_connect(client_handler, client_unit);
     if (clithd_try_cnntask_ret < 0) {
         if (clithd_try_cnntask_ret == PROGRAM_INPROGRESS) {
             return PROGRAM_OK;
@@ -114,35 +271,27 @@ int32_t client_thread_retry_connect(struct ClientUnit *client_unit, struct Clien
 
     ++(client_unit->curr_connect);
 
-    struct sockaddr_in server_addr;
-    socklen_t server_addr_len = sizeof(server_addr);
+    sockaddr_t server_addr;
+    socklen_t server_addr_len = client_unit->ip.addr_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
     if (getpeername(client_handler->fd, (struct sockaddr *)&server_addr, &server_addr_len) < 0) {
         PRINT_ERROR("client can't socket peername %d! ", errno);
         return PROGRAM_FAULT;
     }
-    client_debug_print("client unit", "connect", server_addr.sin_addr.s_addr, server_addr.sin_port, client_unit->debug);
 
-    int32_t client_ask_ret = client_ask(client_handler, client_unit->pktlen, client_unit->api, client_unit->domain, client_unit->groupip ? client_unit->groupip:client_unit->ip, client_unit->port);
-    if (client_ask_ret == PROGRAM_FAULT) {
-        --client_unit->curr_connect;
-        struct epoll_event ep_ev;
-        if (epoll_ctl(client_unit->epfd, EPOLL_CTL_DEL, client_handler->fd, &ep_ev) < 0) {
-            PRINT_ERROR("client can't delete socket '%d' to control epoll %d! ", client_handler->fd, errno);
-            return PROGRAM_FAULT;
-        }
-    } else if (client_ask_ret == PROGRAM_ABORT) {
-        --client_unit->curr_connect;
-        if (close(client_handler->fd) < 0) {
-            PRINT_ERROR("client can't close the socket %d! ", errno);
-            return PROGRAM_FAULT;
-        }
-        client_debug_print("client unit", "close", server_addr.sin_addr.s_addr, server_addr.sin_port, client_unit->debug);
-    } else {
-        client_unit->send_bytes += client_unit->pktlen;
-        client_debug_print("client unit", "send", server_addr.sin_addr.s_addr, server_addr.sin_port, client_unit->debug);
+    // sockaddr to ip, port
+    ip_addr_t remote_ip;
+    uint16_t remote_port = ((struct sockaddr_in*)&server_addr)->sin_port;
+    if (((struct sockaddr *)&server_addr)->sa_family == AF_INET) {
+        remote_ip.addr_family = AF_INET;
+        remote_ip.u_addr.ip4 = ((struct sockaddr_in *)&server_addr)->sin_addr;
+    } else if (((struct sockaddr *)&server_addr)->sa_family == AF_INET6) {
+        remote_ip.addr_family = AF_INET6;
+        remote_ip.u_addr.ip6 = ((struct sockaddr_in6 *)&server_addr)->sin6_addr;
     }
 
-    return PROGRAM_OK;
+    client_debug_print("client unit", "connect", &remote_ip, remote_port, client_unit->debug);
+
+    return client_process_ask(client_handler, client_unit);
 }
 
 // the single thread, client connects and gets epoll feature descriptors
@@ -162,7 +311,7 @@ int32_t client_thread_create_epfd_and_reg(struct ClientUnit *client_unit)
     }
 
     for (uint32_t i = 0; i < connect_num; ++i) {
-        int32_t clithd_try_cnntask_ret = client_thread_try_connect(client_unit->handlers + i, client_unit->epfd, client_unit->ip, client_unit->groupip, client_unit->port, client_unit->sport, client_unit->domain, client_unit->api);
+        int32_t clithd_try_cnntask_ret = client_thread_try_connect(client_unit->handlers + i, client_unit);
         if (clithd_try_cnntask_ret < 0) {
             if (clithd_try_cnntask_ret == PROGRAM_INPROGRESS) {
                 continue;
@@ -179,26 +328,11 @@ int32_t client_thread_create_epfd_and_reg(struct ClientUnit *client_unit)
 
             ++(client_unit->curr_connect);
 
-            client_debug_print("client unit", "connect", client_unit->ip, client_unit->port, client_unit->debug);
-
-            int32_t client_ask_ret = client_ask(client_unit->handlers + i, client_unit->pktlen, client_unit->api, client_unit->domain, client_unit->groupip ? client_unit->groupip:client_unit->ip, client_unit->port);
-            if (client_ask_ret == PROGRAM_FAULT) {
-                --client_unit->curr_connect;
-                struct epoll_event ep_ev;
-                if (epoll_ctl(client_unit->epfd, EPOLL_CTL_DEL, (client_unit->handlers + i)->fd, &ep_ev) < 0) {
-                    PRINT_ERROR("client can't delete socket '%d' to control epoll %d! ", client_unit->epevs[i].data.fd, errno);
-                    return PROGRAM_FAULT;
-                }
-            } else if (client_ask_ret == PROGRAM_ABORT) {
-                --client_unit->curr_connect;
-                if (close((client_unit->handlers + i)->fd) < 0) {
-                    PRINT_ERROR("client can't close the socket! ");
-                    return PROGRAM_FAULT;
-                }
-                client_debug_print("client unit", "close", client_unit->ip, client_unit->port, client_unit->debug);
-            } else {
-                client_unit->send_bytes += client_unit->pktlen;
-                client_debug_print("client unit", "send", client_unit->ip, client_unit->port, client_unit->debug);
+            client_debug_print("client unit", "connect", &client_unit->ip, client_unit->port, client_unit->debug);
+
+            int32_t client_ask_ret = client_process_ask(client_unit->handlers + i, client_unit);
+            if (client_ask_ret != PROGRAM_OK) {
+                return client_ask_ret;
             }
         }
     }
@@ -206,15 +340,97 @@ int32_t client_thread_create_epfd_and_reg(struct ClientUnit *client_unit)
     return PROGRAM_OK;
 }
 
+
+static int32_t clithd_proc_epevs_epollout(struct epoll_event *curr_epev, struct ClientUnit *client_unit)
+{
+    int32_t connect_error = 0;
+    socklen_t connect_error_len = sizeof(connect_error);
+    struct ClientHandler *client_handler = (struct ClientHandler *)curr_epev->data.ptr;
+    if (getsockopt(client_handler->fd, SOL_SOCKET, SO_ERROR, (void *)(&connect_error), &connect_error_len) < 0) {
+        PRINT_ERROR("client can't get socket option %d! ", errno);
+        return PROGRAM_FAULT;
+    }
+    if (connect_error < 0) {
+        if (connect_error == ETIMEDOUT) {
+            if (client_thread_retry_connect(client_unit, client_handler) < 0) {
+                return PROGRAM_FAULT;
+            }
+            return PROGRAM_OK;
+        }
+        PRINT_ERROR("client connect error %d! ", connect_error);
+        return PROGRAM_FAULT;
+    } else {
+        ++(client_unit->curr_connect);
+
+        sockaddr_t server_addr;
+        socklen_t server_addr_len =
+            client_unit->ip.addr_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+        if (getpeername(client_handler->fd, (struct sockaddr *)&server_addr, &server_addr_len) < 0) {
+            PRINT_ERROR("client can't socket peername %d! ", errno);
+            return PROGRAM_FAULT;
+        }
+
+        // sockaddr to ip, port
+        ip_addr_t remote_ip;
+        uint16_t remote_port = ((struct sockaddr_in *)&server_addr)->sin_port;
+        if (((struct sockaddr *)&server_addr)->sa_family == AF_INET) {
+            remote_ip.addr_family = AF_INET;
+            remote_ip.u_addr.ip4 = ((struct sockaddr_in *)&server_addr)->sin_addr;
+        } else if (((struct sockaddr *)&server_addr)->sa_family == AF_INET6) {
+            remote_ip.addr_family = AF_INET6;
+            remote_ip.u_addr.ip6 = ((struct sockaddr_in6 *)&server_addr)->sin6_addr;
+        }
+
+        client_debug_print("client unit", "connect", &remote_ip, remote_port, client_unit->debug);
+
+        int32_t client_ask_ret = client_process_ask(client_handler, client_unit);
+        if (client_ask_ret != PROGRAM_OK) {
+            return client_ask_ret;
+        }
+    }
+    return PROGRAM_OK;
+}
+
+static int32_t clithd_proc_epevs_epollin(struct epoll_event *curr_epev, struct ClientUnit *client_unit)
+{
+    ip_addr_t *chkans_ip = client_unit->protocol_type_mode == UDP_MULTICAST ? &client_unit->groupip : &client_unit->ip;
+    int32_t client_chkans_ret = client_chkans((struct ClientHandler *)curr_epev->data.ptr, client_unit->pktlen,
+                                              client_unit->verify, client_unit->api, client_unit->domain, chkans_ip);
+    struct ClientHandler *client_handler = (struct ClientHandler *)curr_epev->data.ptr;
+    int32_t fd = client_handler->fd;
+    if (client_chkans_ret == PROGRAM_FAULT) {
+        --client_unit->curr_connect;
+        struct epoll_event ep_ev;
+        if (epoll_ctl(client_unit->epfd, EPOLL_CTL_DEL, fd, &ep_ev) < 0) {
+            PRINT_ERROR("client can't delete socket '%d' to control epoll %d! ", fd, errno);
+            return PROGRAM_FAULT;
+        }
+    } else if (client_chkans_ret == PROGRAM_ABORT) {
+        --client_unit->curr_connect;
+        if (close(fd) < 0) {
+            PRINT_ERROR("client can't close the socket %d! ", errno);
+            return PROGRAM_FAULT;
+        }
+        client_debug_print("client unit", "close", &client_unit->ip, client_unit->port, client_unit->debug);
+    } else {
+        client_unit->threadVolume.send_bytes += client_unit->pktlen;
+        client_handler->sendtime_interverl = 0;
+        client_debug_print("client unit", "receive", &client_unit->ip, client_unit->port, client_unit->debug);
+    }
+    return PROGRAM_OK;
+}
+
 // the single thread, client processes epoll events
 int32_t clithd_proc_epevs(struct ClientUnit *client_unit)
 {
     int32_t epoll_nfds = epoll_wait(client_unit->epfd, client_unit->epevs, CLIENT_EPOLL_SIZE_MAX, CLIENT_EPOLL_WAIT_TIMEOUT);
+    int ret = 0;
     if (epoll_nfds < 0) {
         PRINT_ERROR("client epoll wait error %d! ", errno);
         return PROGRAM_FAULT;
     }
 
+
     for (int32_t i = 0; i < epoll_nfds; ++i) {
         struct epoll_event *curr_epev = client_unit->epevs + i;
 
@@ -222,76 +438,17 @@ int32_t clithd_proc_epevs(struct ClientUnit *client_unit)
             PRINT_ERROR("client epoll wait error! %d", curr_epev->events);
             return PROGRAM_FAULT;
         } else if (curr_epev->events == EPOLLOUT) {
-            int32_t connect_error = 0;
-            socklen_t connect_error_len = sizeof(connect_error);
-            struct ClientHandler *client_handler = (struct ClientHandler *)curr_epev->data.ptr;
-            if (getsockopt(client_handler->fd, SOL_SOCKET, SO_ERROR, (void *)(&connect_error), &connect_error_len) < 0) {
-                PRINT_ERROR("client can't get socket option %d! ", errno);
-                return PROGRAM_FAULT;
-            }
-            if (connect_error < 0) {
-                if (connect_error == ETIMEDOUT) {
-                    if (client_thread_retry_connect(client_unit, client_handler) < 0) {
-                        return PROGRAM_FAULT;
-                    }
-                    continue;
-                }
-                PRINT_ERROR("client connect error %d! ", connect_error);
-                return PROGRAM_FAULT;
-            } else {
-                ++(client_unit->curr_connect);
-
-                struct sockaddr_in server_addr;
-                socklen_t server_addr_len = sizeof(server_addr);
-                if (getpeername(client_handler->fd, (struct sockaddr *)&server_addr, &server_addr_len) < 0) {
-                    PRINT_ERROR("client can't socket peername %d! ", errno);
-                    return PROGRAM_FAULT;
-                }
-                client_debug_print("client unit", "connect", server_addr.sin_addr.s_addr, server_addr.sin_port, client_unit->debug);
-                
-                int32_t client_ask_ret = client_ask(client_handler, client_unit->pktlen, client_unit->api, client_unit->domain, client_unit->groupip ? client_unit->groupip:client_unit->ip, client_unit->port);
-                if (client_ask_ret == PROGRAM_FAULT) {
-                    --client_unit->curr_connect;
-                    struct epoll_event ep_ev;
-                    if (epoll_ctl(client_unit->epfd, EPOLL_CTL_DEL, curr_epev->data.fd, &ep_ev) < 0) {
-                        PRINT_ERROR("client can't delete socket '%d' to control epoll %d! ", curr_epev->data.fd, errno);
-                        return PROGRAM_FAULT;
-                    }
-                } else if (client_ask_ret == PROGRAM_ABORT) {
-                    --client_unit->curr_connect;
-                    if (close(curr_epev->data.fd) < 0) {
-                        PRINT_ERROR("client can't close the socket! ");
-                        return PROGRAM_FAULT;
-                    }
-                    client_debug_print("client unit", "close", server_addr.sin_addr.s_addr, server_addr.sin_port, client_unit->debug);
-                } else {
-                    client_unit->send_bytes += client_unit->pktlen;
-                    client_debug_print("client unit", "send", server_addr.sin_addr.s_addr, server_addr.sin_port, client_unit->debug);
-                }
+            ret = clithd_proc_epevs_epollout(curr_epev, client_unit);
+            if (ret != PROGRAM_OK) {
+                return ret;
             }
         } else if (curr_epev->events == EPOLLIN) {
-            int32_t client_chkans_ret = client_chkans((struct ClientHandler *)curr_epev->data.ptr, client_unit->pktlen, client_unit->verify, client_unit->api, client_unit->domain, client_unit->groupip ? client_unit->groupip:client_unit->ip);
-            if (client_chkans_ret == PROGRAM_FAULT) {
-                --client_unit->curr_connect;
-                struct epoll_event ep_ev;
-                if (epoll_ctl(client_unit->epfd, EPOLL_CTL_DEL, curr_epev->data.fd, &ep_ev) < 0) {
-                    PRINT_ERROR("client can't delete socket '%d' to control epoll %d! ", curr_epev->data.fd, errno);
-                    return PROGRAM_FAULT;
-                }
-            } else if (client_chkans_ret == PROGRAM_ABORT) {
-                --client_unit->curr_connect;
-                if (close(curr_epev->data.fd) < 0) {
-                    PRINT_ERROR("client can't close the socket %d! ", errno);
-                    return PROGRAM_FAULT;
-                }
-                client_debug_print("client unit", "close", client_unit->ip, client_unit->port, client_unit->debug);
-            } else {
-                client_unit->send_bytes += client_unit->pktlen;
-                client_debug_print("client unit", "receive", client_unit->ip, client_unit->port, client_unit->debug);
+            ret = clithd_proc_epevs_epollin(curr_epev, client_unit);
+            if (ret != PROGRAM_OK) {
+                return ret;
             }
         }
     }
-
     return PROGRAM_OK;
 }
 
@@ -299,6 +456,17 @@ int32_t clithd_proc_epevs(struct ClientUnit *client_unit)
 void *client_s_create_and_run(void *arg)
 {
     struct ClientUnit *client_unit = (struct ClientUnit *)arg;
+    // update domain ip info.
+    client_get_domain_ipversion(client_unit->protocol_type_mode, client_unit);
+
+    if (client_unit->protocol_type_mode == UDP_MULTICAST) {
+        client_unit->threadVolume.ip_type_info = IPV4_MULTICAST;
+    } else {
+        client_unit->threadVolume.ip_type_info = (client_unit->ip.addr_family == AF_INET ? IPV4_STR : IPV6_STR);
+    }
+    client_unit->threadVolume.thread_id = pthread_self();
+
+    client_unit->threadVolume.domain = client_unit->domain;
 
     if (client_thread_create_epfd_and_reg(client_unit) < 0) {
        exit(PROGRAM_FAULT);
@@ -316,6 +484,42 @@ void *client_s_create_and_run(void *arg)
     return (void *)PROGRAM_OK;
 }
 
+// prase the specific supported TCP IP types by cfg_mode.
+static void client_get_protocol_type_by_cfgmode(uint8_t mode, int32_t *support_type_array, int32_t buff_len,
+                                                int32_t *actual_len)
+{
+    int32_t index = 0;
+    for (uint8_t i = V4_TCP; i < PROTOCOL_MODE_MAX; i++) {
+        if (i == V6_UDP) {
+            continue;
+        }
+        if (getbit_num(mode, i) == 1) {
+            if (index >= buff_len) {
+                PRINT_ERROR("index is over, index =%d", index);
+                return;
+            }
+            support_type_array[index] = i;
+            index++;
+        }
+    }
+    *actual_len = index;
+}
+
+static void client_get_domain_ipversion(uint8_t protocol_type, struct ClientUnit *client_unit)
+{
+    client_unit->domain = g_cfgmode_map[protocol_type].domain;
+    client_unit->ip.addr_family = g_cfgmode_map[protocol_type].ip_family;
+}
+
+static void alarm_init()
+{
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = &timer_handle;
+    sigaction(SIGALRM, &sa, NULL);
+    alarm(TIME_SCAN_INTERVAL);
+}
+
 // create client and run
 int32_t client_create_and_run(struct ProgramParams *params)
 {
@@ -323,16 +527,44 @@ int32_t client_create_and_run(struct ProgramParams *params)
     const uint32_t thread_num = params->thread_num;
     pthread_t *tids = (pthread_t *)malloc(thread_num * sizeof(pthread_t));
     struct Client *client = (struct Client *)malloc(sizeof(struct Client));
+    g_client_begin = client;
+    client->threadNum = thread_num;
+
     struct ClientUnit *client_unit = (struct ClientUnit *)malloc(sizeof(struct ClientUnit));
+    memset_s(client_unit, sizeof(struct ClientUnit), 0, sizeof(struct ClientUnit));
+    int32_t protocol_support_array[PROTOCOL_MODE_MAX] = {0};
+    int32_t number_of_support_type = 1;
 
     if (pthread_mutex_init(&client_debug_mutex, NULL) < 0) {
         PRINT_ERROR("client can't init posix mutex %d! ", errno);
         return PROGRAM_FAULT;
     }
 
+    bool v4_cfg_flag = (strcmp(params->ip, PARAM_DEFAULT_IP) != 0);
+    bool v6_cfg_flag = (strcmp(params->ipv6, PARAM_DEFAULT_IP_V6) != 0);
+    bool multcact_cfg_flag = (strcmp(params->groupip, PARAM_DEFAULT_GROUPIP) != 0);
+
+    bool mixed_mode_flag = false;
+    if ((strchr(params->domain, ',') != NULL) || (v4_cfg_flag && v6_cfg_flag) ||
+        (multcact_cfg_flag && (v4_cfg_flag || v6_cfg_flag))) {
+        mixed_mode_flag = true;
+    }
+
     client->uints = client_unit;
     client->debug = params->debug;
 
+    uint8_t protocol_type_mode = program_get_protocol_mode_by_domain_ip(params->domain, params->ip, params->ipv6,
+                                                                        params->groupip);
+
+    client_get_protocol_type_by_cfgmode(protocol_type_mode, protocol_support_array, PROTOCOL_MODE_MAX,
+        &number_of_support_type);
+
+    uint32_t port = UNIX_TCP_PORT_MIN;
+    uint32_t sport = 0;
+    uint32_t sp = 0;
+
+    alarm_init();
+
     for (uint32_t i = 0; i < thread_num; ++i) {
         client_unit->handlers = (struct ClientHandler *)malloc(connect_num * sizeof(struct ClientHandler));
         for (uint32_t j = 0; j < connect_num; ++j) {
@@ -342,13 +574,42 @@ int32_t client_create_and_run(struct ProgramParams *params)
         client_unit->epfd = -1;
         client_unit->epevs = (struct epoll_event *)malloc(CLIENT_EPOLL_SIZE_MAX * sizeof(struct epoll_event));
         client_unit->curr_connect = 0;
-        client_unit->send_bytes = 0;
-        client_unit->ip = inet_addr(params->ip);
-        client_unit->groupip = inet_addr(params->groupip);
-        client_unit->port = htons(params->port);
-        client_unit->sport = htons(params->sport);
+
+        client_unit->threadVolume.cur_connect_num = 0;
+        client_unit->threadVolume.thread_id = 0;
+        client_unit->threadVolume.send_bytes = 0;
+        client_unit->threadVolume.ip_type_info = INVAILD_STR;
+        client_unit->threadVolume.domain = INVAILD_STR;
+
+        client_unit->ip.addr_family = params->addr_family;
+        inet_pton(AF_INET, params->ip, &client_unit->ip.u_addr.ip4);
+        inet_pton(AF_INET6, params->ipv6, &client_unit->ip.u_addr.ip6);
+        client_unit->groupip.addr_family = AF_INET;
+        inet_pton(AF_INET, params->groupip, &client_unit->groupip.u_addr);
+        client_unit->groupip_interface.addr_family = params->addr_family;
+        inet_pton(AF_INET, params->groupip_interface, &client_unit->groupip_interface.u_addr);
+
+        /* loop to set ports to each client_units */
+        while (!((params->port)[port])) {
+            port = (port + 1) % UNIX_TCP_PORT_MAX;
+        }
+        client_unit->port = htons(port++);
+
+        sp = sport;
+        sport++;
+        while (!((params->sport)[sport]) && (sport != sp)) {
+            sport = (sport + 1) % UNIX_TCP_PORT_MAX;
+        }
+
+        client_unit->sport = htons(sport);
         client_unit->connect_num = params->connect_num;
         client_unit->pktlen = params->pktlen;
+        if (strcmp(params->as, "loop") == 0) {
+            client_unit->loop = 1;
+        } else {
+            client_unit->loop = 0;
+        }
+
         client_unit->verify = params->verify;
         client_unit->domain = params->domain;
         client_unit->api = params->api;
@@ -357,6 +618,16 @@ int32_t client_create_and_run(struct ProgramParams *params)
         client_unit->next = (struct ClientUnit *)malloc(sizeof(struct ClientUnit));
         memset_s(client_unit->next, sizeof(struct ClientUnit), 0, sizeof(struct ClientUnit));
 
+        if (number_of_support_type > 0) {
+            int32_t index = i % number_of_support_type;
+            client_unit->protocol_type_mode = protocol_support_array[index];
+        }
+        if (client_unit->protocol_type_mode == V4_UDP || client_unit->protocol_type_mode == V6_UDP ||
+            client_unit->protocol_type_mode == UDP_MULTICAST) {
+            client_unit->pktlen = params->pktlen > UDP_PKTLEN_MAX ? UDP_PKTLEN_MAX : params->pktlen;
+        } else {
+            client_unit->pktlen = params->pktlen;
+        }
         if (pthread_create((tids + i), NULL, client_s_create_and_run, client_unit) < 0) {
             PRINT_ERROR("client can't create thread of poisx %d! ", errno);
             return PROGRAM_FAULT;
@@ -367,9 +638,34 @@ int32_t client_create_and_run(struct ProgramParams *params)
     if (client->debug == false) {
         printf("[program informations]: \n\n");
     }
+
+    struct ThreadUintInfo *beginVolume = (struct ThreadUintInfo *)malloc(thread_num * sizeof(struct ThreadUintInfo));
+    if (beginVolume == NULL) {
+        return PROGRAM_FAULT;
+    }
+    memset_s(beginVolume, thread_num * sizeof(struct ThreadUintInfo), 0, thread_num * sizeof(struct ThreadUintInfo));
+    struct ThreadUintInfo *endVolume = (struct ThreadUintInfo *)malloc(thread_num * sizeof(struct ThreadUintInfo));
+    if (endVolume == NULL) {
+        return PROGRAM_FAULT;
+    }
+    memset_s(endVolume, thread_num * sizeof(struct ThreadUintInfo), 0, thread_num * sizeof(struct ThreadUintInfo));
+
+    if (strcmp(params->as, "loop") == 0) {
+        client->loop = true;
+    }
+
     while (true) {
-        client_info_print(client);
+	if (strcmp(params->as, "loop") == 0) {
+            loop_info_print();
+	}
+        if (mixed_mode_flag == true) {
+            client_info_print_mixed(client, beginVolume, endVolume);
+        } else {
+            client_info_print(client);
+        }
     }
+    free(beginVolume);
+    free(endVolume);
 
     pthread_mutex_destroy(&client_debug_mutex);
 
diff --git a/examples/src/parameter.c b/examples/src/parameter.c
index 1bb6858..7f519e7 100644
--- a/examples/src/parameter.c
+++ b/examples/src/parameter.c
@@ -13,6 +13,8 @@
 
 #include "parameter.h"
 
+static int32_t g_inject_delay[INJECT_DELAY_MAX] = {0};
+static int32_t g_inject_skip[INJECT_SKIP_MAX];
 
 // program short options
 const char prog_short_opts[] = \
@@ -30,9 +32,11 @@ const char prog_short_opts[] = \
     "r"         // ringpmd
     "d"         // debug
     "h"         // help
-    "E"         // epollcreate
-    "C"         // accept
+    "E:"         // epollcreate
+    "C:"         // accept
     "g:"        // group address
+    "k:"        // tcp keep_alive
+    "I:"        // fault inject
     ;
 
 // program long options
@@ -55,17 +59,72 @@ const struct ProgramOption prog_long_opts[] = \
     {PARAM_NAME_EPOLLCREATE, REQUIRED_ARGUMETN, NULL, PARAM_NUM_EPOLLCREATE},
     {PARAM_NAME_ACCEPT, REQUIRED_ARGUMETN, NULL, PARAM_NUM_ACCEPT},
     {PARAM_NAME_GROUPIP, REQUIRED_ARGUMETN, NULL, PARAM_NUM_GROUPIP},
+    {PARAM_NAME_KEEPALIVE, REQUIRED_ARGUMETN, NULL, PARAM_NUM_KEEPALIVE},
+    {PARAM_NAME_INJECT, REQUIRED_ARGUMETN, NULL, PARAM_NUM_INJECT},
 };
 
 
 // get long options
 int getopt_long(int argc, char * const argv[], const char *optstring, const struct ProgramOption *long_opts, int *long_idx);
+// index [0,7)
+uint8_t getbit_num(uint8_t mode, uint8_t index)
+{
+    return (mode & ((uint8_t)1 << index)) != 0;
+}
+
+uint8_t setbitnum_on(uint8_t mode, uint8_t index)
+{
+    mode |=  ((uint8_t)1 << index);
+    return mode;
+}
+
+uint8_t setbitnum_off(uint8_t mode, uint8_t index)
+{
+    mode &=  ~((uint8_t)1 << index);
+    return mode;
+}
+
+static uint8_t program_set_protocol_mode(uint8_t protocol_mode, char *ipv4, char *ipv6, uint8_t index_v4,
+                                         uint8_t index_v6)
+{
+    uint8_t protocol_mode_temp = protocol_mode;
+    if (strcmp(ipv4, PARAM_DEFAULT_IP) != 0) {
+        protocol_mode_temp = setbitnum_on(protocol_mode_temp, index_v4);
+    }
+    if (strcmp(ipv6, PARAM_DEFAULT_IP_V6) != 0) {
+        protocol_mode_temp = setbitnum_on(protocol_mode_temp, index_v6);
+    }
+    return protocol_mode_temp;
+}
+
+uint8_t program_get_protocol_mode_by_domain_ip(char* domain, char* ipv4, char* ipv6, char* groupip)
+{
+    uint8_t protocol_mode = 0;
+    char *cur_ptr = NULL;
+    char *next_Ptr = NULL;
+    cur_ptr = strtok_s(domain, ",", &next_Ptr);
+    while (cur_ptr) {
+        if (strcmp(cur_ptr, "tcp") == 0) {
+            protocol_mode = program_set_protocol_mode(protocol_mode, ipv4, ipv6, V4_TCP, V6_TCP);
+        } else if (strcmp(cur_ptr, "udp") == 0) {
+            protocol_mode = program_set_protocol_mode(protocol_mode, ipv4, ipv6, V4_UDP, V6_UDP);
+        } else if (strcmp(cur_ptr, "unix") == 0) {
+            protocol_mode = setbitnum_on(protocol_mode, UNIX);
+        }
+        cur_ptr = strtok_s(NULL, ",", &next_Ptr);
+    }
+
+    if (strcmp(groupip, PARAM_DEFAULT_GROUPIP) != 0) {
+        protocol_mode = setbitnum_on(protocol_mode, UDP_MULTICAST);
+    }
 
+    return protocol_mode;
+}
 
 // set `as` parameter
 void program_param_parse_as(struct ProgramParams *params)
 {
-    if (strcmp(optarg, "server") == 0 || strcmp(optarg, "client") == 0) {
+    if (strcmp(optarg, "server") == 0 || strcmp(optarg, "client") == 0 || strcmp(optarg, "loop") == 0) {
         params->as = optarg;
     } else {
         PRINT_ERROR("illigal argument -- %s \n", optarg);
@@ -73,40 +132,113 @@ void program_param_parse_as(struct ProgramParams *params)
     }
 }
 
-// set `ip` parameter
-void program_param_parse_ip(struct ProgramParams *params)
+bool ip_is_v6(const char *cp)
+{
+    if (cp != NULL) {
+        const char *c;
+        for (c = cp; *c != 0; c++) {
+            if (*c == ':') {
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+
+static bool program_ipv4_check(char *ipv4)
 {
-    if (inet_addr(optarg) != INADDR_NONE) {
-        params->ip = optarg;
+    in_addr_t ip = ntohl(inet_addr(ipv4));
+    if (ip == INADDR_NONE) {
+        PRINT_ERROR("illigal argument -- %s \n", ipv4);
+        return false;
+    }
+    if ((ip >= ntohl(inet_addr("1.0.0.1"))   && ip <= ntohl(inet_addr("126.255.255.254"))) ||
+        (ip >= ntohl(inet_addr("127.0.0.1")) && ip <= ntohl(inet_addr("127.255.255.254"))) ||
+        (ip >= ntohl(inet_addr("128.0.0.1")) && ip <= ntohl(inet_addr("191.255.255.254"))) ||
+        (ip >= ntohl(inet_addr("192.0.0.1")) && ip <= ntohl(inet_addr("223.255.255.254"))) ||
+        (ip >= ntohl(inet_addr("224.0.0.1")) && ip <= ntohl(inet_addr("224.255.255.255"))) ) { // Broadcast IP
+        return true;
+    }
+
+    PRINT_ERROR("illigal argument -- %s \n", ipv4);
+    return false;
+}
+
+static void program_param_parse_ipv4_addr(char* v4ip_addr, struct ProgramParams *params)
+{
+    struct in6_addr ip_tmp;
+    params->addr_family = AF_INET;
+    if (inet_pton(params->addr_family, v4ip_addr, &ip_tmp) > 0 && program_ipv4_check(v4ip_addr) == true) {
+        params->ip = v4ip_addr;
     } else {
-        PRINT_ERROR("illigal argument -- %s \n", optarg);
+        PRINT_ERROR("illegal ipv4 addr -- %s \n", v4ip_addr);
         exit(PROGRAM_ABORT);
     }
 }
 
-// set `port` parameter
-void program_param_parse_port(struct ProgramParams *params)
+static void program_param_parse_ipv6_addr(char* v6ip_add, struct ProgramParams *params)
 {
-    int32_t port_arg = strtol(optarg, NULL, 0);
-    printf("%d\n", port_arg);
-    if (CHECK_VAL_RANGE(port_arg, UNIX_TCP_PORT_MIN, UNIX_TCP_PORT_MAX) == true) {
-        params->port = (uint32_t)port_arg;
+    struct in6_addr ip_tmp;
+    params->addr_family = AF_INET6;
+    if (inet_pton(AF_INET6, v6ip_add, &ip_tmp) > 0) {
+        params->ipv6 = v6ip_add;
     } else {
-        PRINT_ERROR("illigal argument -- %s \n", optarg);
+        PRINT_ERROR("illegal ipv6 addr -- %s \n", v6ip_add);
         exit(PROGRAM_ABORT);
     }
 }
+// set `ip` parameter,支持同时配置 ipv4 和 ipv6 地址，格式为 ipv4,ipv6
+void program_param_parse_ip(struct ProgramParams *params)
+{
+    char *cur_ptr = NULL;
+    char *next_ptr = NULL;
+
+    cur_ptr = strtok_s(optarg, ",", &next_ptr);
+    while (cur_ptr) {
+        if (ip_is_v6(cur_ptr)) {
+            program_param_parse_ipv6_addr(cur_ptr, params);
+        } else {
+            program_param_parse_ipv4_addr(cur_ptr, params);
+        }
+        cur_ptr = strtok_s(NULL, ",", &next_ptr);
+    }
+}
+
+// set `port` parameter
+void program_param_parse_port(struct ProgramParams *params)
+{
+    char* port_list = optarg;
+    char* token = NULL;
+    int32_t port_arg = 0;
+    params->port[PARAM_DEFAULT_PORT] = 0;
+
+    while ((token = strtok_r(port_list, ",", &port_list))) {
+        port_arg = strtol(token, NULL, 0);
+        if (CHECK_VAL_RANGE(port_arg, UNIX_TCP_PORT_MIN, UNIX_TCP_PORT_MAX) == true) {
+            params->port[port_arg] = 1;
+        } else {
+            PRINT_ERROR("illigal argument -- %s \n", optarg);
+            exit(PROGRAM_ABORT);
+        }
+    }
+}
 
 // set `sport` parameter
 void program_param_parse_sport(struct ProgramParams *params)
 {
-    int32_t sport_arg = strtol(optarg, NULL, 0);
-    printf("%d\n", sport_arg);
-    if (CHECK_VAL_RANGE(sport_arg, UNIX_TCP_PORT_MIN, UNIX_TCP_PORT_MAX) == true) {
-        params->sport = (uint32_t)sport_arg;
-    } else {
-        PRINT_ERROR("illigal argument -- %s \n", optarg);
-        exit(PROGRAM_ABORT);
+    char* port_list = optarg;
+    char* token = NULL;
+    int32_t port_arg = 0;
+
+    while ((token = strtok_r(port_list, ",", &port_list))) {
+        port_arg = strtol(token, NULL, 0);
+        if (CHECK_VAL_RANGE(port_arg, UNIX_TCP_PORT_MIN, UNIX_TCP_PORT_MAX) == true) {
+            params->sport[port_arg] = 1;
+        } else {
+            PRINT_ERROR("illigal argument -- %s \n", optarg);
+            exit(PROGRAM_ABORT);
+        }
     }
 }
 
@@ -148,12 +280,23 @@ void program_param_parse_threadnum(struct ProgramParams *params)
 // set `domain` parameter
 void program_param_parse_domain(struct ProgramParams *params)
 {
-    if (strcmp(optarg, "unix") == 0 || strcmp(optarg, "tcp") == 0 || strcmp(optarg, "udp") == 0) {
-        params->domain = optarg;
-    } else {
-        PRINT_ERROR("illigal argument -- %s \n", optarg);
+    char temp[100] = {0};
+    int32_t ret = strcpy_s(temp, sizeof(temp) / sizeof(char), optarg);
+    if (ret != 0) {
+        PRINT_ERROR("strcpy_s fail ret=%d \n", ret);
         exit(PROGRAM_ABORT);
     }
+    char *cur_ptr = temp;
+    char *next_ptr = NULL;
+    cur_ptr = strtok_s(cur_ptr, ",", &next_ptr);
+    while (cur_ptr) {
+        if (strcmp(cur_ptr, "tcp") != 0 && strcmp(cur_ptr, "udp") != 0 && strcmp(cur_ptr, "unix") != 0) {
+            PRINT_ERROR("illigal argument -- %s \n", cur_ptr);
+            exit(PROGRAM_ABORT);
+        }
+        cur_ptr = strtok_s(NULL, ",", &next_ptr);
+    }
+    params->domain = optarg;
 }
 
 // set `api` parameter
@@ -174,6 +317,9 @@ void program_param_parse_pktlen(struct ProgramParams *params)
     int32_t pktlen_arg = strtol(optarg, NULL, 0);
     if (CHECK_VAL_RANGE(pktlen_arg, MESSAGE_PKTLEN_MIN, MESSAGE_PKTLEN_MAX) == true) {
         params->pktlen = (uint32_t)pktlen_arg;
+        if (strstr(params->domain, "udp") && params->pktlen > UDP_PKTLEN_MAX) {
+            PRINT_WARNNING("udp message too long, change it to %d \n", UDP_PKTLEN_MAX);
+        }
     } else {
         PRINT_ERROR("illigal argument -- %s \n", optarg);
         exit(PROGRAM_ABORT);
@@ -202,16 +348,196 @@ void program_param_parse_accept(struct ProgramParams *params)
     }
 }
 
+// set `tcp_keepalive_idle` parameter
+void program_param_parse_keepalive(struct ProgramParams *params)
+{
+    char *token = NULL;
+    char *next_token = NULL;
+    token = strtok_s(optarg, ",", &next_token);
+    if (token == NULL) {
+        PRINT_ERROR("parse keep_alive idle null, illigal argument(%s) \n", optarg);
+        exit(PROGRAM_ABORT);
+    }
+
+    int32_t keep_alive_idle = strtol(optarg, NULL, 0);
+    if (keep_alive_idle > 0 && keep_alive_idle <= TCP_KEEPALIVE_IDLE_MAX) {
+        params->tcp_keepalive_idle = keep_alive_idle;
+    } else {
+        PRINT_ERROR("keep_alive_idle=%d,illigal argument -- %s \n", keep_alive_idle, optarg);
+        exit(PROGRAM_ABORT);
+    }
+
+    token = strtok_s(NULL, ",", &next_token);
+    if (token == NULL) {
+        PRINT_ERROR("parse keep_alive interval null, illigal argument(%s) \n", optarg);
+        exit(PROGRAM_ABORT);
+    }
+    int32_t keep_alive_interval = strtol(token, NULL, 0);
+    if (keep_alive_interval > 0 && keep_alive_interval <= TCP_KEEPALIVE_IDLE_MAX) {
+        params->tcp_keepalive_interval = keep_alive_interval;
+    } else {
+        PRINT_ERROR("keep_alive_interval=%d,illigal argument -- %s \n", keep_alive_interval, optarg);
+        exit(PROGRAM_ABORT);
+    }
+}
+
 // set `group ip` parameter
 void program_param_parse_groupip(struct ProgramParams *params)
 {
-    in_addr_t ip = inet_addr(optarg);
-    if (ip != INADDR_NONE && ip >= inet_addr("224.0.0.0") && ip <= inet_addr("239.255.255.255")) {
-        params->groupip = optarg;
+    char *cur_ptr = NULL;
+    char *next_ptr = NULL;
+
+    cur_ptr = strtok_s(optarg, ",", &next_ptr);
+    if (program_ipv4_check(cur_ptr) == false) {
+        PRINT_ERROR("illigal argument -- %s \n", cur_ptr);
+        exit(PROGRAM_ABORT);
+    }
+
+    in_addr_t ip = ntohl(inet_addr(cur_ptr));
+    if (ip != INADDR_NONE && ip >= ntohl(inet_addr("224.0.0.0")) && ip <= ntohl(inet_addr("239.255.255.255"))) {
+        params->groupip = cur_ptr;
     } else {
-        PRINT_ERROR("illigal argument -- %s \n", optarg);
+        PRINT_ERROR("illigal argument -- %s \n", cur_ptr);
+        exit(PROGRAM_ABORT);
+    }
+
+    if (*next_ptr) {
+        if (program_ipv4_check(next_ptr)) {
+            params->groupip_interface = next_ptr;
+        } else {
+            PRINT_ERROR("illigal argument -- %s \n", next_ptr);
+            exit(PROGRAM_ABORT);
+        }
+    }
+}
+
+void fault_inject_delay(delay_type type)
+{
+    if (g_inject_delay[type]) {
+        printf("FAULT INJECT: Delay begin, sleep %d seconds.\n", g_inject_delay[type]);
+        sleep(g_inject_delay[type]);
+        g_inject_delay[type] = 0;
+        printf("FAULT INJECT: Delay finished.\n");
+    }
+}
+
+
+// apply fault inject type of delay
+static void delay_param_parse(struct ProgramParams *params)
+{
+    int32_t time = 0;
+    if (params->inject[INJECT_TIME_IDX] != NULL) {
+        time = atoi(params->inject[INJECT_TIME_IDX]);
+    }
+    if (time <= 0) {
+        PRINT_ERROR("FAULT INJECT: delay time input error! receive: \"%s\"\n", params->inject[INJECT_TIME_IDX]);
+        exit(PROGRAM_ABORT);
+    }
+    
+    char *location = params->inject[INJECT_LOCATION_IDX];
+    if (location == NULL) {
+        PRINT_ERROR("FAULT INJECT: Lack param for delay fault inject, The location is not appointed.\n");
         exit(PROGRAM_ABORT);
     }
+    
+    if (strcmp("before_accept", location) == 0) {
+        g_inject_delay[INJECT_DELAY_ACCEPT] = time;
+        return;
+    }
+    if (strcmp("before_read", location) == 0) {
+        g_inject_delay[INJECT_DELAY_READ] = time;
+        return;
+    }
+    if (strcmp("before_write", location) == 0) {
+        g_inject_delay[INJECT_DELAY_WRITE] = time;
+        return;
+    }
+    if (strcmp("before_read_and_write", location) == 0) {
+        g_inject_delay[INJECT_DELAY_READ] = time;
+        g_inject_delay[INJECT_DELAY_WRITE] = time;
+        return;
+    }
+
+    PRINT_ERROR("FAULT INJECT: Unidentified fault inject location -- %s \n", location);
+    exit(PROGRAM_ABORT);
+}
+
+// apply fault inject type of skip
+static void skip_param_parse(struct ProgramParams *params)
+{
+    char* location = params->inject[INJECT_SKIP_IDX];
+    if (location == NULL) {
+        PRINT_ERROR("FAULT INJECT: Lack param for skip fault inject, location is not appointed.\n");
+        exit(PROGRAM_ABORT);
+    }
+
+    if (strcmp("read", location) == 0) {
+        g_inject_skip[INJECT_SKIP_READ] = 1;
+        return;
+    }
+    if (strcmp("write", location) == 0) {
+        g_inject_skip[INJECT_SKIP_WRITE] = 1;
+        return;
+    }
+    if (strcmp("read_and_write", location) == 0) {
+        g_inject_skip[INJECT_SKIP_READ] = 1;
+        g_inject_skip[INJECT_SKIP_WRITE] = 1;
+        return;
+    }
+
+    PRINT_ERROR("FAULT INJECT: Unidentified fault inject location -- %s \n", location);
+    exit(PROGRAM_ABORT);
+}
+
+// judge if need skip fault inject
+int32_t get_g_inject_skip(skip_type type)
+{
+    return g_inject_skip[type];
+}
+
+// check legitimacy of fault injection and apply it.
+static void apply_fault_inject(struct ProgramParams *params)
+{
+    char *inject_type = params->inject[INJECT_TYPE_IDX];
+    if (strcmp("delay", inject_type) == 0) {
+        delay_param_parse(params);
+        return;
+    }
+    if (strcmp("skip", inject_type) == 0) {
+        skip_param_parse(params);
+        return;
+    }
+    
+    PRINT_ERROR("FAULT INJCET: Unidentified fault inject -- %s \n", inject_type);
+    exit(PROGRAM_ABORT);
+}
+
+// set `fault injection` parameter
+static void program_param_parse_inject(struct ProgramParams *params)
+{
+    int32_t inject_idx = 0;
+    char *inject_input = strdup(optarg);
+    if (inject_input == NULL) {
+        PRINT_ERROR("FAULT INJCET: Insufficient memory, strdup failed.\n");
+        exit(PROGRAM_ABORT);
+    }
+    
+    char *context = NULL;
+    char *elem = strtok_s(inject_input, " ", &context);
+    if (elem == NULL) {
+        PRINT_ERROR("FAULT INJECT: Input error. -- %s \n", inject_input);
+        exit(PROGRAM_ABORT);
+    }
+    while (elem != NULL) {
+        if (inject_idx == FAULT_INJECT_PARA_COUNT) {
+            PRINT_ERROR("FAULT INJECT: Exceed the max count (3) of fault inject params. -- %s\n", optarg);
+            exit(PROGRAM_ABORT);
+        }
+        params->inject[inject_idx++] = elem;
+        elem = strtok_s(NULL, " ", &context);
+    }
+
+    apply_fault_inject(params);
 }
 
 // initialize the parameters
@@ -219,8 +545,11 @@ void program_params_init(struct ProgramParams *params)
 {
     params->as = PARAM_DEFAULT_AS;
     params->ip = PARAM_DEFAULT_IP;
-    params->port = PARAM_DEFAULT_PORT;
-    params->sport = PARAM_DEFAULT_SPORT;
+    params->ipv6 = PARAM_DEFAULT_IP_V6;
+    params->addr_family = PARAM_DEFAULT_ADDR_FAMILY;
+    memset_s(params->port, sizeof(bool)*UNIX_TCP_PORT_MAX, 0, sizeof(bool)*UNIX_TCP_PORT_MAX);
+    memset_s(params->sport, sizeof(bool)*UNIX_TCP_PORT_MAX, 0, sizeof(bool)*UNIX_TCP_PORT_MAX);
+    (params->port)[PARAM_DEFAULT_PORT] = 1;
     params->model = PARAM_DEFAULT_MODEL;
     params->thread_num = PARAM_DEFAULT_THREAD_NUM;
     params->connect_num = PARAM_DEFAULT_CONNECT_NUM;
@@ -233,15 +562,19 @@ void program_params_init(struct ProgramParams *params)
     params->epollcreate = PARAM_DEFAULT_EPOLLCREATE;
     params->accept = PARAM_DEFAULT_ACCEPT;
     params->groupip = PARAM_DEFAULT_GROUPIP;
+    params->groupip_interface = PARAM_DEFAULT_GROUPIP;
+    params->tcp_keepalive_idle = PARAM_DEFAULT_KEEPALIVEIDLE;
+    params->tcp_keepalive_interval = PARAM_DEFAULT_KEEPALIVEIDLE;
 }
 
 // print program helps
 void program_params_help(void)
 {
     printf("\n");
-    printf("-a, --as [server | client]: set programas server or client. \n");
+    printf("-a, --as [server | client | loop]: set programas server, client or loop. \n");
     printf("    server: as server. \n");
     printf("    client: as client. \n");
+    printf("    loop: as server and client. \n");
     printf("-i, --ip [???.???.???.???]: set ip address. \n");
     printf("-g, --groupip [???.???.???.???]: set group ip address. \n");
     printf("-p, --port [????]: set port number in range of %d - %d. \n", UNIX_TCP_PORT_MIN, UNIX_TCP_PORT_MAX);
@@ -268,6 +601,16 @@ void program_params_help(void)
     printf("-h, --help: see helps. \n");
     printf("-E, --epollcreate [ec | ec1]: epoll_create method. \n");
     printf("-C, --accept [ac | ac4]: accept method. \n");
+    printf("-k, --keep_alive [keep_alive_idle:keep_alive_interval]: set tcp-alive info in range of %d-%d. \n",
+           PARAM_DEFAULT_KEEPALIVEIDLE, TCP_KEEPALIVE_IDLE_MAX);
+    printf("-I, --inject [\"fault_inject_param0 fault_inject_param1 fault_inject_param2\"]: fault inject\n");
+    printf("    for example: \"delay 20 before_accept\"\n");
+    printf("                 \"delay 20 before_read\"\n");
+    printf("                 \"delay 20 before_write\"\n");
+    printf("                 \"delay 20 before_read_and_write\"\n");
+    printf("                 \"skip read\"\n");
+    printf("                 \"skip write\"\n");
+    printf("                 \"skip read_and_write\"\n");
     printf("\n");
 }
 
@@ -295,7 +638,7 @@ int32_t program_params_parse(struct ProgramParams *params, uint32_t argc, char *
             case (PARAM_NUM_PORT):
                 program_param_parse_port(params);
                 break;
-	    case (PARAM_NUM_SPORT):
+            case (PARAM_NUM_SPORT):
                 program_param_parse_sport(params);
                 break;
             case (PARAM_NUM_MODEL):
@@ -331,9 +674,15 @@ int32_t program_params_parse(struct ProgramParams *params, uint32_t argc, char *
             case (PARAM_NUM_ACCEPT):
                 program_param_parse_accept(params);
                 break;
-	    case (PARAM_NUM_GROUPIP):
-	        program_param_parse_groupip(params);
-		break;
+            case (PARAM_NUM_GROUPIP):
+                program_param_parse_groupip(params);
+                break;
+            case (PARAM_NUM_KEEPALIVE):
+                program_param_parse_keepalive(params);
+                break;
+            case (PARAM_NUM_INJECT):
+                program_param_parse_inject(params);
+                break;
             case (PARAM_NUM_HELP):
                 program_params_help();
                 return PROGRAM_ABORT;
@@ -345,11 +694,6 @@ int32_t program_params_parse(struct ProgramParams *params, uint32_t argc, char *
         }
     }
 
-    if (strcmp(params->domain, "tcp") != 0) {
-        params->thread_num = 1;
-        params->connect_num = 1;
-    }
-
     return PROGRAM_OK;
 }
 
@@ -361,22 +705,47 @@ void program_params_print(struct ProgramParams *params)
     printf("--> [as]:                       %s \n", params->as);
     if (strcmp(params->groupip, PARAM_DEFAULT_GROUPIP) != 0) {
         if (strcmp(params->as, "server") == 0) {
-            printf("--> [server ip]:                %s \n", params->ip);
             printf("--> [server group ip]:          %s \n", params->groupip);
+            printf("--> [server groupip_interface]: %s \n", params->groupip_interface);
         } else {
-            printf("--> [server ip]:                %s \n", params->groupip);
-            printf("--> [client send ip]:           %s \n", params->ip);
+            printf("--> [client group ip]:          %s \n", params->groupip);
+            printf("--> [client groupip_interface]: %s \n", params->groupip_interface);
         }
-    } else {
-        printf("--> [server ip]:                %s \n", params->ip);
     }
-    if ((strcmp(params->as, "server") == 0 && strcmp(params->groupip, PARAM_DEFAULT_GROUPIP)) != 0) {
-        printf("--> [server group ip]:          %s \n", params->groupip);
+    printf("--> [server ip]:                %s \n", params->ip);
+    if (strcmp(params->ipv6, PARAM_DEFAULT_IP_V6) != 0) {
+        printf("--> [server ipv6]:              %s \n", params->ipv6);
+    }
+
+    printf("--> [server port]:              ");
+    uint32_t comma = 0;
+    uint32_t sport = 0;
+
+    /* use comma to print port list */
+    for (uint32_t i = UNIX_TCP_PORT_MIN; i < UNIX_TCP_PORT_MAX; i++) {
+        if ((params->port)[i]) {
+            printf("%s%u", comma?",":"", i);
+            comma = 1;
+        }
+        if ((params->sport)[i]) {
+            sport = i;
+        }
     }
-    printf("--> [server port]:              %u \n", params->port);
-    if (params->sport && strcmp(params->as, "client") == 0) {
-        printf("--> [client sport]:             %u \n", params->sport);
+    printf(" \n");
+
+    /* use comma to print sport list */
+    if (sport && strcmp(params->as, "client") == 0) {
+        printf("--> [client sport]:             ");
+        comma = 0;
+        for (uint32_t i = UNIX_TCP_PORT_MIN; i < sport + 1; i++) {
+            if ((params->sport)[i]) {
+                printf("%s%u", comma?",":"", i);
+                comma = 1;
+            }
+        }
+        printf(" \n");
     }
+
     if (strcmp(params->as, "server") == 0) {
         printf("--> [model]:                    %s \n", params->model);
     }
@@ -404,5 +773,16 @@ void program_params_print(struct ProgramParams *params)
     printf("--> [debug]:                    %s \n", (params->debug == true) ? "on" : "off");
     printf("--> [epoll create]:             %s \n", params->epollcreate);
     printf("--> [accept]:                   %s \n", params->accept);
+    printf("--> [inject]:                   ");
+    if (params->inject[INJECT_TYPE_IDX] == NULL) {
+        printf("none \n");
+    } else {
+        for (int32_t i = 0; i < FAULT_INJECT_PARA_COUNT; ++i) {
+            if (params->inject[i] != NULL) {
+                printf("%s ", params->inject[i]);
+            }
+        }
+	printf("\n");
+    }
     printf("\n");
 }
diff --git a/examples/src/server.c b/examples/src/server.c
index 8634dde..7bc7d9e 100644
--- a/examples/src/server.c
+++ b/examples/src/server.c
@@ -14,20 +14,22 @@
 #include "server.h"
 
 static pthread_mutex_t server_debug_mutex;      // the server mutex for debug
+struct LoopInfo loopmod;
 
 // server debug information print
-void server_debug_print(const char *ch_str, const char *act_str, in_addr_t ip, uint16_t port, bool debug)
+void server_debug_print(const char *ch_str, const char *act_str, ip_addr_t *ip, uint16_t port, bool debug)
 {
     if (debug == true) {
         pthread_mutex_lock(&server_debug_mutex);
-        struct in_addr sin_addr;
-        sin_addr.s_addr = ip;
+        uint8_t str_len = ip->addr_family == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
+        char str_ip[str_len];
+        inet_ntop(ip->addr_family, &ip->u_addr, str_ip, str_len);
         PRINT_SERVER("[%s] [pid: %d] [tid: %ld] [%s <- %s:%d]. ", \
                     ch_str, \
                     getpid(), \
                     pthread_self(), \
                     act_str, \
-                    inet_ntoa(sin_addr), \
+                    str_ip, \
                     ntohs(port));
         pthread_mutex_unlock(&server_debug_mutex);
     }
@@ -37,7 +39,7 @@ void server_debug_print(const char *ch_str, const char *act_str, in_addr_t ip, u
 void sermud_info_print(struct ServerMud *server_mud)
 {
     if (server_mud->debug == false) {
-        uint32_t curr_connect = server_mud->curr_connect;
+        uint32_t curr_connect = 0;
 
         struct timeval begin;
         gettimeofday(&begin, NULL);
@@ -48,6 +50,7 @@ void sermud_info_print(struct ServerMud *server_mud)
         struct ServerMudWorker *begin_uint = server_mud->workers;
         while (begin_uint != NULL) {
             begin_recv_bytes += begin_uint->recv_bytes;
+            curr_connect += begin_uint->curr_connect;
             begin_uint = begin_uint->next;
         }
 
@@ -122,45 +125,82 @@ int32_t sermud_listener_create_epfd_and_reg(struct ServerMud *server_mud)
     }
 
     struct epoll_event ep_ev;
-    ep_ev.data.ptr = (void *)&(server_mud->listener);
     ep_ev.events = EPOLLIN | EPOLLET;
-    if (epoll_ctl(server_mud->epfd, EPOLL_CTL_ADD, server_mud->listener.fd, &ep_ev) < 0) {
-        PRINT_ERROR("server can't control epoll %d! ", errno);
-        return PROGRAM_FAULT;
+    for (int i = 0; i < PROTOCOL_MODE_MAX; i++) {
+        if (server_mud->listener.listen_fd_array[i] != -1) {
+            struct ServerHandler *server_handler = (struct ServerHandler *)malloc(sizeof(struct ServerHandler));
+            memset_s(server_handler, sizeof(struct ServerHandler), 0, sizeof(struct ServerHandler));
+            server_handler->fd = server_mud->listener.listen_fd_array[i];
+            ep_ev.data.ptr = (void *)server_handler;
+            if (epoll_ctl(server_mud->epfd, EPOLL_CTL_ADD, server_mud->listener.listen_fd_array[i], &ep_ev) < 0) {
+                PRINT_ERROR("epoll_ctl failed %d! listen_fd=%d ", errno, server_mud->listener.listen_fd_array[i]);
+                return PROGRAM_FAULT;
+            }
+        }
+    }
+
+    return PROGRAM_OK;
+}
+
+static void sermud_accept_get_remote_ip(sockaddr_t *accept_addr, ip_addr_t *remote_ip, bool is_tcp_v6_flag)
+{
+    remote_ip->addr_family = is_tcp_v6_flag ? AF_INET6 : AF_INET;
+    if (is_tcp_v6_flag == false) {
+        remote_ip->u_addr.ip4 = ((struct sockaddr_in *)accept_addr)->sin_addr;
+    } else {
+        remote_ip->u_addr.ip6 = ((struct sockaddr_in6 *)accept_addr)->sin6_addr;
     }
+}
 
-    server_debug_print("server mud listener", "waiting", server_mud->ip, server_mud->port, server_mud->debug);
+int32_t sermud_set_socket_opt(int32_t accept_fd, struct ServerMud *server_mud)
+{
+    if (set_tcp_keep_alive_info(accept_fd, server_mud->tcp_keepalive_idle, server_mud->tcp_keepalive_interval) < 0) {
+        PRINT_ERROR("cant't set_tcp_keep_alive_info! ");
+        return PROGRAM_FAULT;
+    }
 
+    if (set_socket_unblock(accept_fd) < 0) {
+        PRINT_ERROR("server can't set the connect socket to unblock! ");
+        return PROGRAM_FAULT;
+    }
     return PROGRAM_OK;
 }
 
 // the listener thread, unblock, dissymmetric server accepts the connections
-int32_t sermud_listener_accept_connects(struct ServerMud *server_mud)
+int32_t sermud_listener_accept_connects(struct epoll_event *curr_epev, struct ServerMud *server_mud)
 {
+    int32_t fd = ((struct ServerHandler*)(curr_epev->data.ptr))->fd;
+    fault_inject_delay(INJECT_DELAY_ACCEPT);
+
     while (true) {
-        struct sockaddr_in accept_addr;
-        uint32_t sockaddr_in_len = sizeof(struct sockaddr_in);
+        sockaddr_t accept_addr;
+        bool is_tcp_v6_flag = (fd == server_mud->listener.listen_fd_array[V6_TCP]) ? true : false;
+
+        uint32_t sockaddr_in_len = is_tcp_v6_flag ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
+
         int32_t accept_fd;
-        if (strcmp(server_mud->domain, "udp") == 0) {
-            break;
-        }
+
+        int32_t listen_fd_index = is_tcp_v6_flag ? V6_TCP : V4_TCP;
+        int32_t listen_fd = server_mud->listener.listen_fd_array[listen_fd_index];
 
         if (strcmp(server_mud->accept, "ac4") == 0) {
-            accept_fd = accept4(server_mud->listener.fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len, SOCK_CLOEXEC);
+            accept_fd = accept4(listen_fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len, SOCK_CLOEXEC);
         } else {
-            accept_fd = accept(server_mud->listener.fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len);
+            accept_fd = accept(listen_fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len);
         }
-        
+
         if (accept_fd < 0) {
             break;
         }
 
-        if (set_socket_unblock(accept_fd) < 0) {
-            PRINT_ERROR("server can't set the connect socket to unblock! ");
+        if (sermud_set_socket_opt(accept_fd, server_mud) < 0) {
             return PROGRAM_FAULT;
         }
 
-        ++(server_mud->curr_connect);
+        // sockaddr to ip, port
+        ip_addr_t remote_ip;
+        uint16_t remote_port = ((struct sockaddr_in *)&accept_addr)->sin_port;
+        sermud_accept_get_remote_ip(&accept_addr, &remote_ip, is_tcp_v6_flag);
 
         pthread_t *tid = (pthread_t *)malloc(sizeof(pthread_t));
         struct ServerMudWorker *worker = (struct ServerMudWorker *)malloc(sizeof(struct ServerMudWorker));
@@ -169,26 +209,50 @@ int32_t sermud_listener_accept_connects(struct ServerMud *server_mud)
         worker->epevs = (struct epoll_event *)malloc(sizeof(struct epoll_event));
         worker->recv_bytes = 0;
         worker->pktlen = server_mud->pktlen;
-        worker->ip = accept_addr.sin_addr.s_addr;
-        worker->port = accept_addr.sin_port;
+        worker->ip = remote_ip;
+        worker->port = remote_port;
         worker->api = server_mud->api;
         worker->debug = server_mud->debug;
         worker->next = server_mud->workers;
         worker->epollcreate = server_mud->epollcreate;
+        worker->worker.is_v6 = is_tcp_v6_flag ? 1 : 0;
+        worker->domain = server_mud->domain;
+        worker->curr_connect = 1;
 
         server_mud->workers = worker;
 
-        if (pthread_create(tid, NULL, sermud_worker_create_and_run, server_mud) < 0) {
+        if (pthread_create(tid, NULL, sermud_worker_create_and_run, worker) < 0) {
             PRINT_ERROR("server can't create poisx thread %d! ", errno);
             return PROGRAM_FAULT;
         }
 
-        server_debug_print("server mud listener", "accept", accept_addr.sin_addr.s_addr, accept_addr.sin_port, server_mud->debug);
+        server_debug_print("server mud listener", "accept", &remote_ip, remote_port, server_mud->debug);
     }
 
     return PROGRAM_OK;
 }
 
+static int32_t server_handler_close(int32_t epfd, struct ServerHandler *server_handler)
+{
+    int32_t fd = server_handler->fd;
+    struct epoll_event ep_ev;
+    if (server_handler) {
+        free(server_handler);
+    }
+
+    if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ep_ev) < 0) {
+        PRINT_ERROR("server can't delete socket '%d' to control epoll %d! ", fd, errno);
+        return PROGRAM_FAULT;
+    }
+
+    if (close(fd) < 0) {
+        PRINT_ERROR("server can't close the socket %d! ", errno);
+        return PROGRAM_FAULT;
+    }
+
+    return 0;
+}
+
 // the worker thread, unblock, dissymmetric server processes the events
 int32_t sermud_worker_proc_epevs(struct ServerMudWorker *worker_unit, const char* domain)
 {
@@ -201,32 +265,60 @@ int32_t sermud_worker_proc_epevs(struct ServerMudWorker *worker_unit, const char
     for (int32_t i = 0; i < epoll_nfds; ++i) {
         struct epoll_event *curr_epev = worker_unit->epevs + i;
 
-        if (curr_epev->events == EPOLLERR || curr_epev->events == EPOLLHUP || curr_epev->events == EPOLLRDHUP) {
+        if (curr_epev->events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+            worker_unit->curr_connect--;
             PRINT_ERROR("server epoll wait error %d! ", curr_epev->events);
-            return PROGRAM_FAULT;
+            if (server_handler_close(worker_unit->epfd, (struct ServerHandler *)curr_epev->data.ptr) != 0) {
+                return PROGRAM_FAULT;
+            }
         }
 
         if (curr_epev->events == EPOLLIN) {
             struct ServerHandler *server_handler = (struct ServerHandler *)curr_epev->data.ptr;
 
-            int32_t server_ans_ret = server_ans(server_handler, worker_unit->pktlen, worker_unit->api, domain);
+            int32_t server_ans_ret = server_ans(server_handler->fd, worker_unit->pktlen, worker_unit->api, "tcp");
             if (server_ans_ret == PROGRAM_FAULT) {
-                struct epoll_event ep_ev;
-                if (epoll_ctl(worker_unit->epfd, EPOLL_CTL_DEL, server_handler->fd, &ep_ev) < 0) {
-                    PRINT_ERROR("server can't delete socket '%d' to control epoll %d! ", server_handler->fd, errno);
+                worker_unit->curr_connect--;
+                if (server_handler_close(worker_unit->epfd, server_handler) != 0) {
                     return PROGRAM_FAULT;
                 }
             } else if (server_ans_ret == PROGRAM_ABORT) {
-                if (close(server_handler->fd) < 0) {
-                    PRINT_ERROR("server can't close the socket %d! ", errno);
+                worker_unit->curr_connect--;
+                server_debug_print("server mud worker", "close", &worker_unit->ip, worker_unit->port, worker_unit->debug);
+                if (server_handler_close(worker_unit->epfd, server_handler) != 0) {
                     return PROGRAM_FAULT;
                 }
-                server_debug_print("server mud worker", "close", worker_unit->ip, worker_unit->port, worker_unit->debug);
             } else {
                 worker_unit->recv_bytes += worker_unit->pktlen;
-                server_debug_print("server mud worker", "receive", worker_unit->ip, worker_unit->port, worker_unit->debug);
+                server_debug_print("server mud worker", "receive", &worker_unit->ip, worker_unit->port, worker_unit->debug);
+            }
+        }
+    }
+
+    return PROGRAM_OK;
+}
+
+static int32_t sermud_process_epollin_event(struct epoll_event *curr_epev, struct ServerMud *server_mud)
+{
+    struct ServerHandler *server_handler = (struct ServerHandler *)curr_epev->data.ptr;
+
+    if (server_handler->fd == server_mud->listener.listen_fd_array[V4_UDP] ||
+        server_handler->fd == server_mud->listener.listen_fd_array[UDP_MULTICAST]) {
+        uint32_t pktlen = server_mud->pktlen > UDP_PKTLEN_MAX ? UDP_PKTLEN_MAX : server_mud->pktlen;
+        int32_t server_ans_ret = server_ans(server_handler->fd, pktlen, server_mud->api, "udp");
+        if (server_ans_ret != PROGRAM_OK) {
+            if (server_handler_close(server_mud->epfd, server_handler) != 0) {
+                PRINT_ERROR("server_handler_close server_ans_ret %d! \n", server_ans_ret);
+                return PROGRAM_FAULT;
             }
         }
+        server_mud->workers->recv_bytes += pktlen;
+    } else {
+        int32_t sermud_listener_accept_connects_ret = sermud_listener_accept_connects(curr_epev, server_mud);
+        if (sermud_listener_accept_connects_ret < 0) {
+            PRINT_ERROR("server try accept error %d! ", sermud_listener_accept_connects_ret);
+            return PROGRAM_FAULT;
+        }
     }
 
     return PROGRAM_OK;
@@ -244,15 +336,14 @@ int32_t sermud_listener_proc_epevs(struct ServerMud *server_mud)
     for (int32_t i = 0; i < epoll_nfds; ++i) {
         struct epoll_event *curr_epev = server_mud->epevs + i;
     
-        if (curr_epev->events == EPOLLERR || curr_epev->events == EPOLLHUP || curr_epev->events == EPOLLRDHUP) {
+        if (curr_epev->events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
             PRINT_ERROR("server epoll wait error %d! ", curr_epev->events);
-            return PROGRAM_FAULT;
+            server_handler_close(server_mud->epfd, (struct ServerHandler *)curr_epev->data.ptr);
+            return PROGRAM_OK;
         }
 
         if (curr_epev->events == EPOLLIN) {
-            int32_t sermud_listener_accept_connects_ret = sermud_listener_accept_connects(server_mud);
-            if (sermud_listener_accept_connects_ret < 0) {
-                PRINT_ERROR("server try accept error %d! ", sermud_listener_accept_connects_ret);
+            if (sermud_process_epollin_event(curr_epev, server_mud) < 0) {
                 return PROGRAM_FAULT;
             }
         }
@@ -266,15 +357,15 @@ void *sermud_worker_create_and_run(void *arg)
 {
     pthread_detach(pthread_self());
 
-    struct ServerMudWorker *worker_unit = ((struct ServerMud *)arg)->workers;
-    char* domain = ((struct ServerMud *)arg)->domain;
+    struct ServerMudWorker *worker_unit = (struct ServerMudWorker *)arg;
+    char *domain = worker_unit->domain;
 
     if (sermud_worker_create_epfd_and_reg(worker_unit) < 0) {
-       exit(PROGRAM_FAULT);
+        return (void *)PROGRAM_OK;
     }
     while (true) {
         if (sermud_worker_proc_epevs(worker_unit, domain) < 0) {
-            exit(PROGRAM_FAULT);
+            return (void *)PROGRAM_OK;
         }
     }
 
@@ -284,26 +375,60 @@ void *sermud_worker_create_and_run(void *arg)
     return (void *)PROGRAM_OK;
 }
 
+void sermud_memory_recycle(struct ServerMud *server_mud)
+{
+    // recycle mem of epevs
+    if (server_mud->epevs) {
+        free(server_mud->epevs);
+    }
+    struct ServerMudWorker *head = server_mud->workers;
+    while (head) {
+        if (head->epevs) {
+            free(head->epevs);
+        }
+        struct ServerMudWorker *next = head->next;
+        free(head);
+        head = next;
+    }
+}
+
 // create the listener thread, unblock, dissymmetric server and run
 void *sermud_listener_create_and_run(void *arg)
 {
     struct ServerMud *server_mud = (struct ServerMud *)arg;
 
-    if (create_socket_and_listen(&(server_mud->listener.fd), server_mud->ip, server_mud->groupip, server_mud->port, server_mud->domain) < 0) {
-        exit(PROGRAM_FAULT);
+    uint32_t port = 0;
+    for (; port < UNIX_TCP_PORT_MAX; port++) {
+        if ((server_mud->port)[port]) {
+            if (create_socket_and_listen(server_mud->listener.listen_fd_array, &(server_mud->server_ip_info),
+                                         htons(port), server_mud->protocol_type_mode) < 0) {
+                PRINT_ERROR("create_socket_and_listen err");
+                sermud_memory_recycle(server_mud);
+                exit(PROGRAM_FAULT);
+            }
+        }
     }
+
     if (sermud_listener_create_epfd_and_reg(server_mud) < 0) {
-       exit(PROGRAM_FAULT);
+        sermud_memory_recycle(server_mud);
+        exit(PROGRAM_FAULT);
     }
     while (true) {
         if (sermud_listener_proc_epevs(server_mud) < 0) {
+            sermud_memory_recycle(server_mud);
             exit(PROGRAM_FAULT);
         }
     }
-    if (close(server_mud->listener.fd) < 0 || close(server_mud->epfd) < 0) {
-        exit(PROGRAM_FAULT);
-    }
 
+    for (int i = 0; i < PROTOCOL_MODE_MAX; i++) {
+        if (server_mud->listener.listen_fd_array[i] == -1)
+            continue;
+        if (close(server_mud->listener.listen_fd_array[i]) < 0) {
+            sermud_memory_recycle(server_mud);
+            exit(PROGRAM_FAULT);
+        }
+    }
+    sermud_memory_recycle(server_mud);
     return (void *)PROGRAM_OK;
 }
 
@@ -319,19 +444,44 @@ int32_t sermud_create_and_run(struct ProgramParams *params)
     }
 
     server_mud->listener.fd = -1;
-    server_mud->workers = NULL;
+    for (int32_t i = 0; i < PROTOCOL_MODE_MAX; i++) {
+        server_mud->listener.listen_fd_array[i] = -1;
+    }
+
+    struct ServerMudWorker *workers = (struct ServerMudWorker *)malloc(sizeof(struct ServerMudWorker));
+    if (workers == NULL) {
+        PRINT_ERROR("malloc truct ServerMudWorker failed ");
+        return PROGRAM_FAULT;
+    }
+    memset_s(workers, sizeof(struct ServerMudWorker), 0, sizeof(struct ServerMudWorker));
+    workers->next = NULL;
+    server_mud->workers = workers;
+
     server_mud->epfd = -1;
     server_mud->epevs = (struct epoll_event *)malloc(SERVER_EPOLL_SIZE_MAX * sizeof(struct epoll_event));
-    server_mud->curr_connect = 0;
-    server_mud->ip = inet_addr(params->ip);
-    server_mud->groupip = inet_addr(params->groupip);
-    server_mud->port = htons(params->port);
+    server_mud->server_ip_info.ip.addr_family = params->addr_family;
+
+    inet_pton(AF_INET, params->ip, &server_mud->server_ip_info.ip.u_addr.ip4);
+    inet_pton(AF_INET6, params->ipv6, &server_mud->server_ip_info.ip.u_addr.ip6);
+
+    server_mud->server_ip_info.groupip.addr_family = params->addr_family;
+    inet_pton(AF_INET, params->groupip, &server_mud->server_ip_info.groupip.u_addr);
+
+    server_mud->server_ip_info.groupip_interface.addr_family = params->addr_family;
+    inet_pton(AF_INET, params->groupip_interface, &server_mud->server_ip_info.groupip_interface.u_addr);
+
+    server_mud->port = params->port;
     server_mud->pktlen = params->pktlen;
-    server_mud->domain = params->domain;
+
+    server_mud->protocol_type_mode = program_get_protocol_mode_by_domain_ip(params->domain, params->ip, params->ipv6,
+                                                                            params->groupip);
+
     server_mud->api = params->api;
     server_mud->debug = params->debug;
     server_mud->epollcreate = params->epollcreate;
     server_mud->accept = params->accept;
+    server_mud->tcp_keepalive_idle = params->tcp_keepalive_idle;
+    server_mud->tcp_keepalive_interval = params->tcp_keepalive_interval;
 
     if (pthread_create(tid, NULL, sermud_listener_create_and_run, server_mud) < 0) {
         PRINT_ERROR("server can't create poisx thread %d! ", errno);
@@ -341,10 +491,17 @@ int32_t sermud_create_and_run(struct ProgramParams *params)
     if (server_mud->debug == false) {
         printf("[program informations]: \n\n");
     }
-    while (true) {
-        sermud_info_print(server_mud);
+
+    if (strcmp(params->as, "server") == 0) {
+        while (true) {
+            sermud_info_print(server_mud);
+        }
+    } else if (strcmp(params->as, "loop") == 0) {
+        loopmod.model = params->model;
+        loopmod.server_mud_info = server_mud;
     }
 
+
     pthread_mutex_destroy(&server_debug_mutex);
 
     return PROGRAM_OK;
@@ -413,39 +570,62 @@ int32_t sersum_create_epfd_and_reg(struct ServerMumUnit *server_unit)
        return PROGRAM_FAULT;
     }
 
-    struct epoll_event ep_ev;
-    ep_ev.data.ptr = (void *)&(server_unit->listener);
+    struct epoll_event ep_ev = {0};
     ep_ev.events = EPOLLIN | EPOLLET;
-    if (epoll_ctl(server_unit->epfd, EPOLL_CTL_ADD, server_unit->listener.fd, &ep_ev) < 0) {
-        PRINT_ERROR("server can't control epoll %d! ", errno);
-        return PROGRAM_FAULT;
+
+    for (int32_t i = 0; i < PROTOCOL_MODE_MAX; i++) {
+        if (server_unit->listener.listen_fd_array[i] != -1) {
+            struct ServerHandler *server_handler = (struct ServerHandler *)malloc(sizeof(struct ServerHandler));
+            memset_s(server_handler, sizeof(struct ServerHandler), 0, sizeof(struct ServerHandler));
+            server_handler->fd = server_unit->listener.listen_fd_array[i];
+
+            ep_ev.data.ptr = (void *)server_handler;
+            if (epoll_ctl(server_unit->epfd, EPOLL_CTL_ADD, server_unit->listener.listen_fd_array[i], &ep_ev) < 0) {
+                PRINT_ERROR("epoll_ctl failed %d! listen_fd=%d ", errno, server_unit->listener.listen_fd_array[i]);
+                return PROGRAM_FAULT;
+            }
+        }
     }
 
-    server_debug_print("server mum unit", "waiting", server_unit->ip, server_unit->port, server_unit->debug);
+    server_debug_print("server mum unit", "waiting", &server_unit->server_ip_info.ip, server_unit->port,
+                       server_unit->debug);
 
     return PROGRAM_OK;
 }
 
 // the single thread, unblock, mutliplexing IO server accepts the connections
-int32_t sersum_accept_connects(struct ServerMumUnit *server_unit, struct ServerHandler *server_handler)
+int32_t sersum_accept_connects(struct epoll_event *cur_epev, struct ServerMumUnit *server_unit)
 {
+    fault_inject_delay(INJECT_DELAY_ACCEPT);
+    int32_t fd = ((struct ServerHandler*)(cur_epev->data.ptr))->fd;
     while (true) {
-        struct sockaddr_in accept_addr;
-        uint32_t sockaddr_in_len = sizeof(struct sockaddr_in);
+        sockaddr_t accept_addr;
+        bool is_tcp_v6 = (fd == (server_unit->listener.listen_fd_array[V6_TCP])) ? true : false;
+
+        socklen_t sockaddr_in_len = is_tcp_v6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
         int32_t accept_fd;
-        if (strcmp(server_unit->domain, "udp") == 0) {
-            break;
-        }
+        int32_t ret = 0;
+
+        int32_t listen_index = (is_tcp_v6) ? V6_TCP : V4_TCP;
+        int32_t listen_fd = server_unit->listener.listen_fd_array[listen_index];
 
         if (strcmp(server_unit->accept, "ac4") == 0) {
-            accept_fd = accept4(server_unit->listener.fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len, SOCK_CLOEXEC);
+            accept_fd = accept4(listen_fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len, SOCK_CLOEXEC);
         } else {
-            accept_fd = accept(server_unit->listener.fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len);
+            accept_fd = accept(listen_fd, (struct sockaddr *)&accept_addr, &sockaddr_in_len);
         }
-        
+
         if (accept_fd < 0) {
+            if (errno != EWOULDBLOCK && errno != EAGAIN){
+                PRINT_ERROR("accept_fd=%d , errno=%d ", accept_fd, errno);
+            }
             break;
         }
+        ret = set_tcp_keep_alive_info(accept_fd, server_unit->tcp_keepalive_idle, server_unit->tcp_keepalive_interval);
+        if (ret < 0) {
+            PRINT_ERROR("set_tcp_keep_alive_info ret=%d \n", ret);
+            return PROGRAM_FAULT;
+        }
 
         if (set_socket_unblock(accept_fd) < 0) {
             PRINT_ERROR("server can't set the connect socket to unblock! ");
@@ -454,6 +634,8 @@ int32_t sersum_accept_connects(struct ServerMumUnit *server_unit, struct ServerH
 
         struct ServerHandler *server_handler = (struct ServerHandler *)malloc(sizeof(struct ServerHandler));
         server_handler->fd = accept_fd;
+        server_handler->is_v6 = (is_tcp_v6) ? 1 : 0;
+
         struct epoll_event ep_ev;
         ep_ev.data.ptr = (void *)server_handler;
         ep_ev.events = EPOLLIN | EPOLLET;
@@ -463,13 +645,98 @@ int32_t sersum_accept_connects(struct ServerMumUnit *server_unit, struct ServerH
         }
 
         ++server_unit->curr_connect;
-        
-        server_debug_print("server mum unit", "accept", accept_addr.sin_addr.s_addr, accept_addr.sin_port, server_unit->debug);
+
+        // sockaddr tp ip, port
+        ip_addr_t remote_ip;
+        uint16_t remote_port = ((struct sockaddr_in*)&accept_addr)->sin_port;
+        remote_ip.addr_family = (is_tcp_v6) ? AF_INET6 : AF_INET;
+        if (is_tcp_v6 == false) {
+            remote_ip.u_addr.ip4 = ((struct sockaddr_in *)&accept_addr)->sin_addr;
+        } else {
+            remote_ip.u_addr.ip6 = ((struct sockaddr_in6 *)&accept_addr)->sin6_addr;
+        }
+
+        server_debug_print("server mum unit", "accept", &remote_ip, remote_port, server_unit->debug);
     }
 
     return PROGRAM_OK;
 }
 
+static int sersum_get_remote_ip(struct ServerHandler *server_handler, ip_addr_t *remote_ip, uint16_t *remote_port)
+{
+    sockaddr_t connect_addr;
+    socklen_t connect_addr_len = server_handler->is_v6 == 0 ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+    if (getpeername(server_handler->fd, (struct sockaddr *)&connect_addr, &connect_addr_len) < 0) {
+        PRINT_ERROR("server can't socket peername %d! ", errno);
+        return PROGRAM_ABORT;
+    }
+
+    *remote_port = ((struct sockaddr_in *)&connect_addr)->sin_port;
+    if (((struct sockaddr *)&connect_addr)->sa_family == AF_INET) {
+        remote_ip->addr_family = AF_INET;
+        remote_ip->u_addr.ip4 = ((struct sockaddr_in *)&connect_addr)->sin_addr;
+    } else if (((struct sockaddr *)&connect_addr)->sa_family == AF_INET6) {
+        remote_ip->addr_family = AF_INET6;
+        remote_ip->u_addr.ip6 = ((struct sockaddr_in6 *)&connect_addr)->sin6_addr;
+    }
+    return PROGRAM_OK;
+}
+
+static int sersum_process_tcp_accept_event(struct ServerMumUnit *server_unit, struct epoll_event *curr_epev)
+{
+    struct ServerHandler *server_handler = (struct ServerHandler *)curr_epev->data.ptr;
+    ip_addr_t remote_ip;
+    uint16_t remote_port;
+
+    if (sersum_get_remote_ip(server_handler, &remote_ip, &remote_port) != PROGRAM_OK) {
+        return PROGRAM_ABORT;
+    }
+
+    int32_t server_ans_ret = server_ans(server_handler->fd, server_unit->pktlen, server_unit->api, "tcp");
+    if (server_ans_ret == PROGRAM_FAULT) {
+        --server_unit->curr_connect;
+        server_handler_close(server_unit->epfd, server_handler);
+    } else if (server_ans_ret == PROGRAM_ABORT) {
+        --server_unit->curr_connect;
+        server_debug_print("server mum unit", "close", &remote_ip, remote_port, server_unit->debug);
+        server_handler_close(server_unit->epfd, server_handler);
+    } else {
+        server_unit->recv_bytes += server_unit->pktlen;
+        server_debug_print("server mum unit", "receive", &remote_ip, remote_port, server_unit->debug);
+    }
+    return PROGRAM_OK;
+}
+
+static int sersum_process_epollin_event(struct ServerMumUnit *server_unit, struct epoll_event *curr_epev)
+{
+    struct ServerHandler *server_handler = (struct ServerHandler *)curr_epev->data.ptr;
+    int32_t fd = server_handler->fd;
+    if (fd == (server_unit->listener.listen_fd_array[V4_TCP]) ||
+        fd == (server_unit->listener.listen_fd_array[V6_TCP])) {
+        int32_t sersum_accept_connects_ret = sersum_accept_connects(curr_epev, server_unit);
+        if (sersum_accept_connects_ret < 0) {
+            PRINT_ERROR("server try accept error %d! ", sersum_accept_connects_ret);
+            return PROGRAM_ABORT;
+        }
+    } else if (fd == (server_unit->listener.listen_fd_array[V4_UDP]) ||
+               fd == (server_unit->listener.listen_fd_array[UDP_MULTICAST])) {
+        uint32_t pktlen = server_unit->pktlen > UDP_PKTLEN_MAX ? UDP_PKTLEN_MAX : server_unit->pktlen;
+        int32_t server_ans_ret = server_ans(fd, pktlen, server_unit->api, "udp");
+        if (server_ans_ret != PROGRAM_OK) {
+            if (server_handler_close(server_unit->epfd, server_handler) != 0) {
+                PRINT_ERROR("server_handler_close ret %d! \n", server_ans_ret);
+                return PROGRAM_ABORT;
+            }
+        }
+        server_unit->recv_bytes += pktlen;
+    } else {
+        if (sersum_process_tcp_accept_event(server_unit, curr_epev) != PROGRAM_OK) {
+            return PROGRAM_ABORT;
+        }
+    }
+    return PROGRAM_OK;
+}
+
 // the single thread, unblock, mutliplexing IO server processes the events
 int32_t sersum_proc_epevs(struct ServerMumUnit *server_unit)
 {
@@ -482,47 +749,16 @@ int32_t sersum_proc_epevs(struct ServerMumUnit *server_unit)
     for (int32_t i = 0; i < epoll_nfds; ++i) {
         struct epoll_event *curr_epev = server_unit->epevs + i;
 
-        if (curr_epev->events == EPOLLERR || curr_epev->events == EPOLLHUP || curr_epev->events == EPOLLRDHUP) {
-            PRINT_ERROR("server epoll wait error %d! ", curr_epev->events);
-            return PROGRAM_FAULT;
+        if (curr_epev->events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+            server_unit->curr_connect--;
+            if (server_handler_close(server_unit->epfd, (struct ServerHandler *)curr_epev->data.ptr) != 0) {
+                return PROGRAM_OK;
+            }
         }
 
         if (curr_epev->events == EPOLLIN) {
-            if (curr_epev->data.ptr == (void *)&(server_unit->listener) && strcmp(server_unit->domain, "udp") != 0) {
-                int32_t sersum_accept_connects_ret = sersum_accept_connects(server_unit, &(server_unit->listener));
-                if (sersum_accept_connects_ret < 0) {
-                    PRINT_ERROR("server try accept error %d! ", sersum_accept_connects_ret);
-                    return PROGRAM_FAULT;
-                }
-                continue;
-            } else {
-                struct ServerHandler *server_handler = (struct ServerHandler *)curr_epev->data.ptr;
-                struct sockaddr_in connect_addr;
-                socklen_t connect_addr_len = sizeof(connect_addr);
-                if (strcmp(server_unit->domain, "udp") != 0 && getpeername(server_handler->fd, (struct sockaddr *)&connect_addr, &connect_addr_len) < 0) {
-                    PRINT_ERROR("server can't socket peername %d! ", errno);
-                    return PROGRAM_FAULT;
-                }
-
-                int32_t server_ans_ret = server_ans(server_handler, server_unit->pktlen, server_unit->api, server_unit->domain);
-                if (server_ans_ret == PROGRAM_FAULT) {
-                    --server_unit->curr_connect;
-                    struct epoll_event ep_ev;
-                    if (epoll_ctl(server_unit->epfd, EPOLL_CTL_DEL, server_handler->fd, &ep_ev) < 0) {
-                        PRINT_ERROR("server can't delete socket '%d' to control epoll %d! ", server_handler->fd, errno);
-                        return PROGRAM_FAULT;
-                    }
-                } else if (server_ans_ret == PROGRAM_ABORT) {
-                    --server_unit->curr_connect;
-                    if (close(server_handler->fd) < 0) {
-                        PRINT_ERROR("server can't close the socket %d! ", errno);
-                        return PROGRAM_FAULT;
-                    }
-                    server_debug_print("server mum unit", "close", connect_addr.sin_addr.s_addr, connect_addr.sin_port, server_unit->debug);
-                } else {
-                    server_unit->recv_bytes += server_unit->pktlen;
-                    server_debug_print("server mum unit", "receive", connect_addr.sin_addr.s_addr, connect_addr.sin_port, server_unit->debug);
-                }
+            if (sersum_process_epollin_event(server_unit, curr_epev) != PROGRAM_OK) {
+                return PROGRAM_ABORT;
             }
         }
     }
@@ -535,7 +771,9 @@ void *sersum_create_and_run(void *arg)
 {
     struct ServerMumUnit *server_unit = (struct ServerMumUnit *)arg;
 
-    if (create_socket_and_listen(&(server_unit->listener.fd), server_unit->ip, server_unit->groupip, server_unit->port, server_unit->domain) < 0) {
+    if (create_socket_and_listen(server_unit->listener.listen_fd_array, &(server_unit->server_ip_info),
+                                 server_unit->port, server_unit->protocol_type_mode) < 0) {
+        PRINT_ERROR("create_socket_and_listen err! \n");
         exit(PROGRAM_FAULT);
     }
     if (sersum_create_epfd_and_reg(server_unit) < 0) {
@@ -560,6 +798,7 @@ int32_t sermum_create_and_run(struct ProgramParams *params)
     pthread_t *tids = (pthread_t *)malloc(thread_num * sizeof(pthread_t));
     struct ServerMum *server_mum = (struct ServerMum *)malloc(sizeof(struct ServerMum));
     struct ServerMumUnit *server_unit = (struct ServerMumUnit *)malloc(sizeof(struct ServerMumUnit));
+    memset_s(server_unit, sizeof(struct ServerMumUnit), 0, sizeof(struct ServerMumUnit));
 
     if (pthread_mutex_init(&server_debug_mutex, NULL) < 0) {
         PRINT_ERROR("server can't init posix mutex %d! ", errno);
@@ -568,22 +807,47 @@ int32_t sermum_create_and_run(struct ProgramParams *params)
 
     server_mum->uints = server_unit;
     server_mum->debug = params->debug;
+    uint32_t port = UNIX_TCP_PORT_MIN;
 
     for (uint32_t i = 0; i < thread_num; ++i) {
         server_unit->listener.fd = -1;
+        for (int32_t i = 0; i < PROTOCOL_MODE_MAX; i++) {
+            server_unit->listener.listen_fd_array[i] = -1;
+        }
         server_unit->epfd = -1;
         server_unit->epevs = (struct epoll_event *)malloc(SERVER_EPOLL_SIZE_MAX * sizeof(struct epoll_event));
         server_unit->curr_connect = 0;
         server_unit->recv_bytes = 0;
-        server_unit->ip = inet_addr(params->ip);
-	server_unit->groupip = inet_addr(params->groupip);
-        server_unit->port = htons(params->port);
+        server_unit->server_ip_info.ip.addr_family = params->addr_family;
+        inet_pton(AF_INET, params->ip, &server_unit->server_ip_info.ip.u_addr.ip4);
+        inet_pton(AF_INET6, params->ipv6, &server_unit->server_ip_info.ip.u_addr.ip6);
+
+        server_unit->server_ip_info.groupip.addr_family = AF_INET;
+        inet_pton(AF_INET, params->groupip, &server_unit->server_ip_info.groupip.u_addr);
+
+        server_unit->server_ip_info.groupip_interface.addr_family = AF_INET;
+        inet_pton(AF_INET, params->groupip_interface, &server_unit->server_ip_info.groupip_interface.u_addr);
+
+        /* loop to set ports to each server_mums */
+        while (!((params->port)[port])) {
+            port = (port + 1) % UNIX_TCP_PORT_MAX;
+        }
+        server_unit->port = htons(port++);
         server_unit->pktlen = params->pktlen;
-        server_unit->domain = params->domain;
+
+        server_unit->protocol_type_mode = program_get_protocol_mode_by_domain_ip(params->domain, params->ip,
+                                                                                 params->ipv6, params->groupip);
+
+        // Create multicast sockets only on the first thread
+        if (i != 0) {
+            server_unit->protocol_type_mode = setbitnum_off(server_unit->protocol_type_mode, UDP_MULTICAST);
+        }
         server_unit->api = params->api;
         server_unit->debug = params->debug;
         server_unit->epollcreate = params->epollcreate;
         server_unit->accept = params->accept;
+        server_unit->tcp_keepalive_idle = params->tcp_keepalive_idle;
+        server_unit->tcp_keepalive_interval = params->tcp_keepalive_interval;
         server_unit->next = (struct ServerMumUnit *)malloc(sizeof(struct ServerMumUnit));
         if (server_unit->next) {
             memset_s(server_unit->next, sizeof(struct ServerMumUnit), 0, sizeof(struct ServerMumUnit));
@@ -599,8 +863,14 @@ int32_t sermum_create_and_run(struct ProgramParams *params)
     if (server_mum->debug == false) {
         printf("[program informations]: \n\n");
     }
-    while (true) {
-        sermum_info_print(server_mum);
+
+    if (strcmp(params->as, "server") == 0) {
+        while (true) {
+            sermum_info_print(server_mum);
+        }
+    } else if (strcmp(params->as, "loop") == 0) {
+        loopmod.model = params->model;
+	loopmod.server_mum_info = server_mum;
     }
 
     pthread_mutex_destroy(&server_debug_mutex);
diff --git a/examples/src/utilities.c b/examples/src/utilities.c
index 7247b44..59d8bea 100644
--- a/examples/src/utilities.c
+++ b/examples/src/utilities.c
@@ -11,35 +11,215 @@
 */
 
 
-#include "utilities.h"
+#include "parameter.h"
 
+int32_t set_tcp_keep_alive_info(int32_t sockfd, int32_t tcp_keepalive_idle, int32_t tcp_keepalive_interval)
+{
+    int32_t ret = 0;
+    int32_t keep_alive = 1;
+    int32_t keep_idle = 1;
+    int32_t keep_interval = 1;
+
+    if ((tcp_keepalive_idle == PARAM_DEFAULT_KEEPALIVEIDLE) ||
+        (tcp_keepalive_interval == PARAM_DEFAULT_KEEPALIVEIDLE)) {
+        return 0;
+    }
+
+    keep_idle = tcp_keepalive_idle;
+    ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive));
+    if (ret != 0) {
+        PRINT_ERROR("setsockopt keep_alive err ret=%d \n", ret);
+        return ret;
+    }
+
+    ret = setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keep_idle, sizeof(keep_idle));
+    if (ret != 0) {
+        PRINT_ERROR("setsockopt keep_idle err ret=%d \n", ret);
+        return ret;
+    }
+
+    keep_interval = tcp_keepalive_interval;
+    ret = setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval));
+    if (ret != 0) {
+        PRINT_ERROR("setsockopt keep_interval err ret=%d \n", ret);
+        return ret;
+    }
+    return ret;
+}
+
+static int32_t process_unix_fd(int32_t *socket_fd, int32_t *listen_fd_array)
+{
+    struct sockaddr_un socket_addr;
+    int32_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (fd < 0) {
+        PRINT_ERROR("can't create socket %d! ", errno);
+        return PROGRAM_FAULT;
+    }
+    *socket_fd = fd;
+
+    unlink(SOCKET_UNIX_DOMAIN_FILE);
+    socket_addr.sun_family = AF_UNIX;
+    strcpy_s(socket_addr.sun_path, sizeof(socket_addr.sun_path), SOCKET_UNIX_DOMAIN_FILE);
+    if (bind(*socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_un)) < 0) {
+        PRINT_ERROR("can't bind the address to socket %d! ", errno);
+        return PROGRAM_FAULT;
+    }
+
+    if (listen(*socket_fd, SERVER_SOCKET_LISTEN_BACKLOG) < 0) {
+        PRINT_ERROR("server socket can't lisiten %d! ", errno);
+        return PROGRAM_FAULT;
+    }
+    return PROGRAM_OK;
+}
+
+static int32_t process_udp_groupip(int32_t fd, ip_addr_t *ip, ip_addr_t *groupip, sockaddr_t *socker_add_info,
+                                   ip_addr_t *groupip_interface)
+{
+    struct ip_mreq mreq;
+    if (groupip->u_addr.ip4.s_addr) {
+        mreq.imr_multiaddr = groupip->u_addr.ip4;
+        if (groupip_interface->u_addr.ip4.s_addr) {
+            mreq.imr_interface = groupip_interface->u_addr.ip4;
+        } else {
+            mreq.imr_interface = ip->u_addr.ip4;
+        }
+
+        if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {
+            PRINT_ERROR("can't set the address to group %d! ", errno);
+            return PROGRAM_FAULT;
+        }
+        ((struct sockaddr_in *)socker_add_info)->sin_addr = groupip->u_addr.ip4;
+        return PROGRAM_OK;
+    }
+    return PROGRAM_OK;
+}
+
+static int32_t server_create_sock(uint8_t protocol_mode, int32_t* fd_arry)
+{
+    bool ret = true;
+    for (int32_t i = 0; i < PROTOCOL_MODE_MAX; i++) {
+        if (getbit_num(protocol_mode, i) == 0)
+            continue;
+        if (i == V4_TCP) {
+            fd_arry[i] = socket(AF_INET, SOCK_STREAM, 0);
+        } else if (i == V6_TCP) {
+            fd_arry[i] = socket(AF_INET6, SOCK_STREAM, 0);
+        } else if (i == V4_UDP) {
+            fd_arry[i] = socket(AF_INET, SOCK_DGRAM, 0);
+        } else if (i == UDP_MULTICAST) {
+            fd_arry[i] = socket(AF_INET, SOCK_DGRAM, 0);
+        } else {
+            continue;
+        }
+        if (fd_arry[i] < 0) {
+            PRINT_ERROR("can't create socket type=%d errno=%d! ", i, errno);
+            ret = false;
+            break;
+        }
+    }
+
+    if (ret == false) {
+        for (int32_t i = 0; i< PROTOCOL_MODE_MAX; i++) {
+            if (fd_arry[i] > 0) {
+                close(fd_arry[i]);
+            }
+        }
+        return PROGRAM_FAULT;
+    }
+    return PROGRAM_OK;
+}
+
+static int32_t socket_add_info_init(int32_t idx, uint16_t port, struct ServerIpInfo *server_ip_info,
+                                    sockaddr_t *socker_add_info, int32_t *listen_fd_array)
+{
+    ip_addr_t *ip = &(server_ip_info->ip);
+    ip_addr_t *groupip = &(server_ip_info->groupip);
+    ip_addr_t *groupip_interface = &(server_ip_info->groupip_interface);
+
+    uint32_t len = ((idx == V4_TCP || idx == V4_UDP || idx == UDP_MULTICAST) ?
+        sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+    memset_s(socker_add_info, len, 0, len);
+
+    if (idx == V4_TCP || idx == V4_UDP) {
+        ((struct sockaddr_in *)socker_add_info)->sin_addr = ip->u_addr.ip4;
+    } else if (idx == V6_TCP) {
+        ((struct sockaddr_in6 *)socker_add_info)->sin6_addr = ip->u_addr.ip6;
+    } else if (idx == UDP_MULTICAST) {
+        if (process_udp_groupip(listen_fd_array[idx], ip, groupip, socker_add_info, groupip_interface) != PROGRAM_OK) {
+            return PROGRAM_FAULT;
+        }
+    }
+
+    ((struct sockaddr *)socker_add_info)->sa_family = ((idx == V4_TCP || idx == V4_UDP || idx == UDP_MULTICAST) ?
+                                                        AF_INET : AF_INET6);
+    ((struct sockaddr_in *)socker_add_info)->sin_port = port;
+    return PROGRAM_OK;
+}
 
 // create the socket and listen
-int32_t create_socket_and_listen(int32_t *socket_fd, in_addr_t ip, in_addr_t groupip, uint16_t port, const char *domain)
+int32_t create_socket_and_listen(int32_t *listen_fd_array, struct ServerIpInfo *server_ip_info,
+                                 uint16_t port, uint8_t protocol_mode)
 {
-    if (strcmp(domain, "tcp") == 0) {
-        *socket_fd = socket(AF_INET, SOCK_STREAM, 0);
-        if (*socket_fd < 0) {
-            PRINT_ERROR("can't create socket %d! ", errno);
+    int32_t port_multi = 1;
+    uint32_t len = 0;
+    sockaddr_t socker_add_info;
+
+    if (getbit_num(protocol_mode, UNIX) == 1) {
+        if (process_unix_fd(&listen_fd_array[UNIX], listen_fd_array) != PROGRAM_OK) {
             return PROGRAM_FAULT;
         }
-    } else if (strcmp(domain, "unix") == 0) {
-        *socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-        if (*socket_fd < 0) {
-            PRINT_ERROR("can't create socket %d! ", errno);
+        return PROGRAM_OK;
+    }
+
+    if (server_create_sock(protocol_mode, listen_fd_array) != PROGRAM_OK) {
+        return PROGRAM_FAULT;
+    }
+
+    for (int32_t i = 0;i< PROTOCOL_MODE_MAX; i++) {
+        if (listen_fd_array[i] <= 0)
+            continue;
+        if (setsockopt(listen_fd_array[i], SOL_SOCKET, SO_REUSEPORT, (void *)&port_multi, sizeof(int32_t)) < 0) {
+            PRINT_ERROR("can't set the option of socket %d! ", errno);
             return PROGRAM_FAULT;
         }
-    } else if (strcmp(domain, "udp") == 0) {
-	*socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
-        if (*socket_fd < 0) {
-            PRINT_ERROR("can't create socket %d! ", errno);
+        if (set_socket_unblock(listen_fd_array[i]) < 0) {
+            PRINT_ERROR("can't set the socket to unblock! ");
+            return PROGRAM_FAULT;
+        }
+        if (socket_add_info_init(i, port, server_ip_info, &socker_add_info, listen_fd_array) != PROGRAM_OK) {
+            return PROGRAM_FAULT;
+        }
+
+        len = ((i == V4_TCP || i == V4_UDP || i == UDP_MULTICAST) ?
+                sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+
+        if (bind(listen_fd_array[i], (struct sockaddr *)&socker_add_info, len) < 0) {
+            PRINT_ERROR("can't bind the address %d!, i=%d, listen_fd_array[i]=%d ", errno, i, listen_fd_array[i]);
             return PROGRAM_FAULT;
         }
+
+        if (i == V4_TCP || i == V6_TCP) {
+            if (listen(listen_fd_array[i], SERVER_SOCKET_LISTEN_BACKLOG) < 0) {
+                PRINT_ERROR("server socket can't lisiten %d! ", errno);
+                return PROGRAM_FAULT;
+            }
+        }
     }
+    return PROGRAM_OK;
+}
 
-    int32_t port_multi = 1;
-    if (setsockopt(*socket_fd, SOL_SOCKET, SO_REUSEPORT, (void *)&port_multi, sizeof(int32_t)) < 0) {
-        PRINT_ERROR("can't set the option of socket %d! ", errno);
+static int32_t creat_socket_init(int32_t *socket_fd, struct ClientUnit *client_unit, sockaddr_t *server_addr)
+{
+    ip_addr_t *ip = &client_unit->ip;
+    const char *domain = client_unit->domain;
+
+    if (strcmp(domain, "tcp") == 0) {
+        *socket_fd = socket(ip->addr_family, SOCK_STREAM, 0);
+    } else {
+        *socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
+    }
+    if (*socket_fd < 0) {
+        PRINT_ERROR("client can't create socket %d! ", errno);
         return PROGRAM_FAULT;
     }
 
@@ -48,106 +228,118 @@ int32_t create_socket_and_listen(int32_t *socket_fd, in_addr_t ip, in_addr_t gro
         return PROGRAM_FAULT;
     }
 
-    if (strcmp(domain, "tcp") == 0) {
-        struct sockaddr_in socket_addr;
-        memset_s(&socket_addr, sizeof(socket_addr), 0, sizeof(socket_addr));
-        socket_addr.sin_family = AF_INET;
-        socket_addr.sin_addr.s_addr = ip;
-        socket_addr.sin_port = port;
-        if (bind(*socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)) < 0) {
-            PRINT_ERROR("can't bind the address to socket %d! ", errno);
-            return PROGRAM_FAULT;
-        }
+    ((struct sockaddr *)server_addr)->sa_family = ip->addr_family;
 
-        if (listen(*socket_fd, SERVER_SOCKET_LISTEN_BACKLOG) < 0) {
-            PRINT_ERROR("server socket can't lisiten %d! ", errno);
-            return PROGRAM_FAULT;
+    return PROGRAM_OK;
+}
+
+static int32_t pocess_connect_sport(int32_t *socket_fd, struct ClientUnit *client_unit, sockaddr_t *server_addr)
+{
+    uint16_t sport = client_unit->sport;
+    ip_addr_t *ip = &client_unit->ip;
+    uint32_t addr_len = ip->addr_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+
+    if (sport) {
+        if (ip->addr_family == AF_INET) {
+            ((struct sockaddr_in *)server_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
+        } else if (ip->addr_family == AF_INET6) {
+            ((struct sockaddr_in6 *)server_addr)->sin6_addr = in6addr_any;
         }
-    } else if (strcmp(domain, "unix") == 0) {
-        struct sockaddr_un socket_addr;
-        unlink(SOCKET_UNIX_DOMAIN_FILE);
-        socket_addr.sun_family = AF_UNIX;
-        strcpy_s(socket_addr.sun_path, sizeof(socket_addr.sun_path), SOCKET_UNIX_DOMAIN_FILE);
-        if (bind(*socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_un)) < 0) {
+        ((struct sockaddr_in *)server_addr)->sin_port = sport;
+        if (bind(*socket_fd, (struct sockaddr *)server_addr, addr_len) < 0) {
             PRINT_ERROR("can't bind the address to socket %d! ", errno);
             return PROGRAM_FAULT;
         }
+    }
+    return PROGRAM_OK;
+}
 
-        if (listen(*socket_fd, SERVER_SOCKET_LISTEN_BACKLOG) < 0) {
-            PRINT_ERROR("server socket can't lisiten %d! ", errno);
+static int32_t pocess_unix_create_connect(int32_t *socket_fd)
+{
+    *socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (*socket_fd < 0) {
+        PRINT_ERROR("client can't create socket %d! ", errno);
+        return PROGRAM_FAULT;
+    }
+
+    struct sockaddr_un server_addr;
+    server_addr.sun_family = AF_UNIX;
+    strcpy_s(server_addr.sun_path, sizeof(server_addr.sun_path), SOCKET_UNIX_DOMAIN_FILE);
+    if (connect(*socket_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) < 0) {
+        if (errno == EINPROGRESS) {
+            return PROGRAM_INPROGRESS;
+        } else {
+            PRINT_ERROR("client can't connect to the server %d! ", errno);
             return PROGRAM_FAULT;
         }
-    } else if (strcmp(domain, "udp") == 0) {
-	struct sockaddr_in socket_addr;
-        memset_s(&socket_addr, sizeof(socket_addr), 0, sizeof(socket_addr));
-        socket_addr.sin_family = AF_INET;
-        socket_addr.sin_port = port;
-
-        if (groupip) {
-            struct ip_mreq mreq;
-            mreq.imr_multiaddr.s_addr = groupip;
-            mreq.imr_interface.s_addr = ip;
-            if (setsockopt(*socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {
-                PRINT_ERROR("can't set the address to group %d! ", errno);
-                return PROGRAM_FAULT;;
+    }
+    return PROGRAM_OK;
+}
+
+static int32_t pocess_udp_multicast(int32_t *socket_fd, struct ClientUnit *client_unit, sockaddr_t *server_addr)
+{
+    const uint32_t loop = client_unit->loop;
+    ip_addr_t *groupip = &client_unit->groupip;
+    if (client_unit->protocol_type_mode == UDP_MULTICAST) {
+        /* set the local device for a multicast socket */
+        ((struct sockaddr_in *)server_addr)->sin_addr = groupip->u_addr.ip4;
+
+        struct in_addr localInterface;
+        localInterface.s_addr = client_unit->groupip_interface.u_addr.ip4.s_addr;
+        if (localInterface.s_addr) {
+            if (setsockopt(*socket_fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface,
+                           sizeof(localInterface)) < 0) {
+                PRINT_ERROR("can't set the multicast interface %d! ", errno);
+                return PROGRAM_FAULT;
             }
-            socket_addr.sin_addr.s_addr = groupip;
-        } else {
-            socket_addr.sin_addr.s_addr = ip;
         }
 
-        if (bind(*socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)) < 0) {
-            PRINT_ERROR("can't bind the address to socket %d! ", errno);
+        /* sent multicast packets should be looped back to the local socket */
+        if (setsockopt(*socket_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) == -1) {
+            PRINT_ERROR("can't set the multicast loop %d! ", errno);
             return PROGRAM_FAULT;
-	}
+        }
     }
-    
     return PROGRAM_OK;
 }
 
 // create the socket and connect
-int32_t create_socket_and_connect(int32_t *socket_fd, in_addr_t ip, in_addr_t groupip, uint16_t port, uint16_t sport, const char *domain, const char *api)
+int32_t create_socket_and_connect(int32_t *socket_fd,  struct ClientUnit *client_unit)
 {
+    ip_addr_t *ip = &client_unit->ip;
+    const char *domain = client_unit->domain;
+    const char *api = client_unit->api;
+
+    sockaddr_t server_addr;
+
     if (strcmp(domain, "tcp") == 0 || strcmp(domain, "udp") == 0) {
-	if (strcmp(domain, "tcp") == 0) {
-            *socket_fd = socket(AF_INET, SOCK_STREAM, 0);
-	} else {
-            *socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
-	}
-        if (*socket_fd < 0) {
-            PRINT_ERROR("client can't create socket %d! ", errno);
+        uint32_t addr_len = ip->addr_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+        memset_s(&server_addr, addr_len, 0, addr_len);
+
+        if (creat_socket_init(socket_fd, client_unit, &server_addr) != PROGRAM_OK) {
             return PROGRAM_FAULT;
         }
 
-        if (set_socket_unblock(*socket_fd) < 0) {
-            PRINT_ERROR("can't set the socket to unblock! ");
+        if (pocess_connect_sport(socket_fd, client_unit, &server_addr) < 0) {
             return PROGRAM_FAULT;
         }
 
-        struct sockaddr_in server_addr;
-        memset_s(&server_addr, sizeof(server_addr), 0, sizeof(server_addr));
-        server_addr.sin_family = AF_INET;
-        if (sport) {
-            server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-            server_addr.sin_port = sport;
-            if (bind(*socket_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) < 0) {
-                PRINT_ERROR("can't bind the address to socket %d! ", errno);
-                return PROGRAM_FAULT;
-            }
+        if (ip->addr_family == AF_INET) {
+            ((struct sockaddr_in *)&server_addr)->sin_addr = ip->u_addr.ip4;
+        } else if (ip->addr_family == AF_INET6) {
+            ((struct sockaddr_in6 *)&server_addr)->sin6_addr = ip->u_addr.ip6;
         }
-        server_addr.sin_addr.s_addr = ip;
-        server_addr.sin_port = port;
+        ((struct sockaddr_in *)&server_addr)->sin_port = client_unit->port;
+
         if (strcmp(domain, "udp") == 0) {
-            if (groupip) {
-                server_addr.sin_addr.s_addr = groupip;
-                if (setsockopt(*socket_fd, IPPROTO_IP, IP_MULTICAST_IF, &ip, sizeof(ip)) != 0) {
-                    PRINT_ERROR("can't set the multicast interface %d! ", errno);
-                    return PROGRAM_FAULT;
-                }
+            int32_t ret = pocess_udp_multicast(socket_fd, client_unit, &server_addr);
+            if (ret != PROGRAM_OK) {
+                return ret;
             }
         }
+
         if (strcmp(domain, "udp") != 0 || strcmp(api, "recvfromsendto") != 0) {
-            if (connect(*socket_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) < 0) {
+            if (connect(*socket_fd, (struct sockaddr *)&server_addr, addr_len) < 0) {
                 if (errno == EINPROGRESS) {
                     return PROGRAM_INPROGRESS;
                 } else {
@@ -157,25 +349,11 @@ int32_t create_socket_and_connect(int32_t *socket_fd, in_addr_t ip, in_addr_t gr
             }
         }
     } else if (strcmp(domain, "unix") == 0) {
-        *socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-        if (*socket_fd < 0) {
-            PRINT_ERROR("client can't create socket %d! ", errno);
-            return PROGRAM_FAULT;
-        }
-
-        struct sockaddr_un server_addr;
-        server_addr.sun_family = AF_UNIX;
-        strcpy_s(server_addr.sun_path, sizeof(server_addr.sun_path), SOCKET_UNIX_DOMAIN_FILE);
-        if (connect(*socket_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) < 0) {
-            if (errno == EINPROGRESS) {
-                return PROGRAM_INPROGRESS;
-            } else {
-                PRINT_ERROR("client can't connect to the server %d! ", errno);
-                return PROGRAM_FAULT;
-            }
+        int32_t ret = pocess_unix_create_connect(socket_fd);
+        if (ret != PROGRAM_OK) {
+            return ret;
         }
     }
-
     return PROGRAM_OK;
 }
 
diff --git a/src/lstack/core/lstack_protocol_stack.c b/src/lstack/core/lstack_protocol_stack.c
index 3e6eeef..e272a04 100644
--- a/src/lstack/core/lstack_protocol_stack.c
+++ b/src/lstack/core/lstack_protocol_stack.c
@@ -655,7 +655,6 @@ int32_t stack_setup_thread(void)
             goto OUT1;
         }
     }
-
     for (uint32_t i = 0; i < queue_num; i++) {
         if (get_global_cfg_params()->seperate_send_recv) {
             if (i % 2 == 0) {
@@ -694,6 +693,7 @@ int32_t stack_setup_thread(void)
     g_stack_group.stack_num = queue_num;
 
     return 0;
+
 OUT1:
     for (int32_t i = 0; i < queue_num; ++i) {
         if (t_params[i] != NULL) {
-- 
2.33.0

