diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6de87bba9ddb0b42997f6ecd3b82b13ec3697435..186815417b360a71e84f45a30e09225b0c71f5e3 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,7 +19,8 @@ if(NOT (${SUBMODULES_NO_BUILD} MATCHES ON))
     add_subdirectory(test)
 endif()
 
-set(DAP_UDP_SERVER_SRCS dap_udp_server.c dap_udp_client.h dap_udp_client.c )
+set(DAP_UDP_SERVER_SRCS dap_udp_server.c dap_udp_client.c dap_dns_server.c)
+set(DAP_UDP_SERVER_HEADERS dap_udp_server.h dap_udp_client.h dap_dns_server.h)
 
 if(WIN32)
   include_directories(../libdap/src/win32/)
@@ -32,6 +33,6 @@ endif()
 
 add_library(${PROJECT_NAME} STATIC ${DAP_UDP_SERVER_SRCS})
 
-target_link_libraries(${PROJECT_NAME} dap_core dap_server_core )
+target_link_libraries(${PROJECT_NAME} dap_core dap_server_core dap_chain_net)
 
 target_include_directories(${PROJECT_NAME} INTERFACE .)
diff --git a/dap_dns_server.c b/dap_dns_server.c
new file mode 100755
index 0000000000000000000000000000000000000000..99bb525ce5f3167ab1d1d9ed7519e81d4466372a
--- /dev/null
+++ b/dap_dns_server.c
@@ -0,0 +1,330 @@
+/*
+ * Authors:
+ * Roman Khlopkov <roman.khlopkov@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://gitlab.demlabs.net
+ * Copyright  (c) 2017-2020
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dap_dns_server.h"
+#include "dap_udp_server.h"
+#include "dap_udp_client.h"
+#include "dap_client_remote.h"
+#include "dap_common.h"
+#include "dap_chain_net.h"
+#include "dap_chain_node.h"
+#include "dap_string.h"
+#include "dap_chain_global_db.h"
+#include "dap_chain_global_db_remote.h"
+
+#define UNUSED(x) (void)(x)
+#define LOG_TAG "dap_dns_server"
+
+static dap_dns_server_t *s_dns_server;
+static char s_root_alias[] = "dnsroot";
+
+/**
+ * @brief dap_dns_buf_init Initialize DNS parser buffer
+ * @param buf DNS buffer structure
+ * @param msg DNS message
+ * @return none
+ */
+void dap_dns_buf_init(dap_dns_buf_t *buf, char *msg) {
+    buf->data = msg;
+    buf->ptr = 0;
+}
+
+/**
+ * @brief dap_dns_buf_get_uint16 Get uint16 from network order
+ * @param buf DNS buffer structure
+ * @return uint16 in host order
+ */
+uint16_t dap_dns_buf_get_uint16(dap_dns_buf_t *buf) {
+    char c;
+    c = buf->data[buf->ptr++];
+    return c << 8 | buf->data[buf->ptr++];
+}
+
+/**
+ * @brief dap_dns_buf_put_uint16 Put uint16 to network order
+ * @param buf DNS buffer structure
+ * @param val uint16 in host order
+ * @return none
+ */
+void dap_dns_buf_put_uint16(dap_dns_buf_t *buf, uint16_t val) {
+    buf->data[buf->ptr++] = val >> 8;
+    buf->data[buf->ptr++] = val;
+}
+
+/**
+ * @brief dap_dns_buf_put_uint32 Put uint32 to network order
+ * @param buf DNS buffer structure
+ * @param val uint32 in host order
+ * @return none
+ */
+void dap_dns_buf_put_uint32(dap_dns_buf_t *buf, uint32_t val) {
+    dap_dns_buf_put_uint16(buf, val >> 16);
+    dap_dns_buf_put_uint16(buf, val);
+}
+
+uint32_t dap_dns_resolve_hostname(char *str) {
+    log_it(L_DEBUG, "DNS parser retrieve hostname %s", str);
+    dap_chain_net_t *l_net = dap_chain_net_by_name("kelvin-testnet");
+    // get nodes list from global_db
+    dap_global_db_obj_t *l_objs = NULL;
+    size_t l_nodes_count = 0;
+    // read all node
+    l_objs = dap_chain_global_db_gr_load(l_net->pub.gdb_nodes, &l_nodes_count);
+    if(!l_nodes_count || !l_objs)
+        return 0;
+    size_t l_node_num = rand() % l_nodes_count;
+    dap_chain_node_info_t *l_node_info = (dap_chain_node_info_t *) l_objs[l_node_num].value;
+    uint32_t addr = l_node_info->hdr.ext_addr_v4.s_addr;
+    dap_chain_global_db_objs_delete(l_objs, l_nodes_count);
+    log_it(L_DEBUG, "DNS resolver find ip %d.%d.%d.%d", addr & 0xFF, (addr >> 8) & 0xFF, (addr >> 16) & 0xFF, (addr >> 24) & 0xFF);
+    return addr;
+}
+
+/**
+ * @brief dap_dns_zone_register Register DNS zone and set callback to handle it
+ * @param zone Name of zone to register
+ * @param callback Callback to handle DNS zone
+ * @return 0 if success, else return error code
+ */
+int dap_dns_zone_register(char *zone, dap_dns_zone_callback_t callback) {
+    dap_dns_zone_hash_t *new_zone;
+    HASH_FIND_STR(s_dns_server->hash_table, zone, new_zone);
+    if (new_zone == NULL) {      // zone is not present
+      new_zone = DAP_NEW(dap_dns_zone_hash_t);
+      new_zone->zone = dap_strdup(zone);
+      HASH_ADD_KEYPTR(hh, s_dns_server->hash_table, new_zone->zone, strlen(new_zone->zone), new_zone);
+    }                           // if zone present, just reassign callback
+    new_zone->callback = callback;
+    return DNS_ERROR_NONE;
+}
+
+/**
+ * @brief dap_dns_zone_unregister Unregister DNS zone
+ * @param zone Name of zone to unregister
+ * @return 0 if success, else return error code
+ */
+int dap_dns_zone_unregister(char *zone) {
+    dap_dns_zone_hash_t *asked_zone;
+    HASH_FIND_STR(s_dns_server->hash_table, zone, asked_zone);
+    if (asked_zone == NULL) {
+        return DNS_ERROR_NAME;
+    }
+    HASH_DEL(s_dns_server->hash_table, asked_zone);
+    DAP_DELETE(asked_zone->zone);
+    DAP_DELETE(asked_zone);
+    return DNS_ERROR_NONE;
+}
+
+/**
+ * @brief dap_dns_zone_find Find callback to registered DNS zone
+ * @param hostname Name of host for which the zone callback being searched
+ * @return Callback for registered DNS zone, else return NULL
+ */
+dap_dns_zone_callback_t dap_dns_zone_find(char *hostname) {
+    dap_dns_zone_hash_t *asked_zone;
+    HASH_FIND_STR(s_dns_server->hash_table, hostname, asked_zone);
+    if (asked_zone == NULL) {
+        if (!strcmp(hostname, &s_root_alias[0])) {
+            return NULL;
+        }
+        char *zone_up = strchr(hostname, '.') + 1;
+        if (zone_up == 1) {
+            zone_up = &s_root_alias[0];
+        }
+        return dap_dns_zone_find(zone_up);
+    } else {
+        return asked_zone->callback;
+    }
+    return NULL;
+}
+
+/**
+ * @brief dap_dns_client_read Read and parse incoming DNS message, send reply to it
+ * @param client DAP client remote structure
+ * @param arg Unused
+ * @return none
+ */
+void dap_dns_client_read(dap_client_remote_t *client, void * arg) {
+    UNUSED(arg);
+    if (client->buf_in_size < DNS_HEADER_SIZE) {        // Bad request
+        return;
+    }
+    dap_dns_buf_t *dns_message = DAP_NEW(dap_dns_buf_t);
+    dap_dns_buf_t *dns_reply = DAP_NEW(dap_dns_buf_t);
+    dns_message->data = DAP_NEW_SIZE(char, client->buf_in_size + 1);
+    dns_message->data[client->buf_in_size] = 0;
+    dap_client_remote_read(client, dns_message->data, client->buf_in_size);
+    dns_message->ptr = 0;
+
+    // Parse incoming DNS message
+    int block_len = DNS_HEADER_SIZE;
+    dns_reply->data = DAP_NEW_SIZE(char, block_len);
+    dns_reply->ptr = 0;
+    uint16_t val = dap_dns_buf_get_uint16(dns_message); // ID
+    dap_dns_buf_put_uint16(dns_reply, val);
+    val = dap_dns_buf_get_uint16(dns_message);          // Flags
+    dns_reply->ptr += sizeof(uint16_t);                 // Put flags later
+    dap_dns_message_flags_t msg_flags;
+    msg_flags.val = val;
+    dap_dns_message_flags_bits_t *flags = &msg_flags.flags;
+    if (flags->qr) {                                     // It's not request
+        goto cleanup;
+    }
+    flags->rcode = DNS_ERROR_NONE;
+    flags->qr = 1;                                       // Response bit set
+    if (flags->tc) {                                     // Truncated messages not supported yet
+        flags->rcode = DNS_ERROR_NOT_SUPPORTED;
+    }
+    flags->ra = 0;                                       // Recursion not supported yet
+    flags->aa = 1;                                       // Authoritative answer
+    uint16_t qdcount = dap_dns_buf_get_uint16(dns_message);
+    dap_dns_buf_put_uint16(dns_reply, qdcount);
+    val = dap_dns_buf_get_uint16(dns_message);          // AN count
+    if (val) {                                          // No other sections should present
+        goto cleanup;
+    }
+    dap_dns_buf_put_uint16(dns_reply, 1);               // 1 answer section
+    val = dap_dns_buf_get_uint16(dns_message);          // NS count
+    if (val) {                                          // No other sections should present
+        goto cleanup;
+    }
+    dap_dns_buf_put_uint16(dns_reply, val);
+    val = dap_dns_buf_get_uint16(dns_message);          // AR count
+    if (val) {                                          // No other sections should present
+        goto cleanup;
+    }
+    dap_dns_buf_put_uint16(dns_reply, val);
+    int dot_count = 0;
+    dap_string_t *dns_hostname = dap_string_new("");
+    for (int i = 0; i < qdcount; i++) {
+        block_len = strlen(&dns_message->data[dns_message->ptr]) + 1 + 2 * sizeof(uint16_t);
+        dns_reply->data = DAP_REALLOC(dns_reply->data, dns_reply->ptr + block_len);
+        memcpy(&dns_reply->data[dns_reply->ptr], &dns_message->data[dns_message->ptr], block_len);
+        dns_reply->ptr += block_len;
+        if (flags->rcode)
+            break;
+        while (dns_message->ptr < dns_reply->ptr - 2 * sizeof(uint16_t)) {
+            uint8_t len = dns_message->data[dns_message->ptr++];
+            if (len > DNS_MAX_DOMAIN_NAME_LEN) {
+                flags->rcode = DNS_ERROR_NAME;
+                break;
+            }
+            if (!len) {
+                break;
+            }
+            if (dot_count) {
+                if (dot_count > 3) {                    // Max three dots allowed
+                    flags->rcode = DNS_ERROR_NAME;
+                    break;
+                }
+                dap_string_append(dns_hostname, ".");
+            }
+            dap_string_append_len(dns_hostname, &dns_message->data[dns_message->ptr], len);
+            dns_message->ptr += len;
+            dot_count++;
+            if (dns_hostname->len >= DNS_MAX_HOSTNAME_LEN) {
+                flags->rcode = DNS_ERROR_NAME;
+                break;
+            }
+        }
+        val = dap_dns_buf_get_uint16(dns_message);      // DNS record type
+        if (val != DNS_RECORD_TYPE_A) {                 // Only host address ipv4
+            flags->rcode = DNS_ERROR_NOT_SUPPORTED;
+            break;
+        }
+        val = dap_dns_buf_get_uint16(dns_message);      // DNS class type
+        if (val != DNS_CLASS_TYPE_IN) {                 // Internet only
+            flags->rcode = DNS_ERROR_NOT_SUPPORTED;
+            break;
+        }
+        if (dns_message->ptr != dns_reply->ptr) {
+            log_it(L_ERROR, "DNS parser pointer unequal, mptr = %u, rptr = %u", dns_message->ptr, dns_reply->ptr);
+        }
+    }
+    // Find ip addr
+    uint32_t ip_addr = 0;
+    if (flags->rcode == DNS_ERROR_NONE) {
+        dap_dns_zone_callback_t callback = dap_dns_zone_find(dns_hostname->str);
+        if (callback) {
+            ip_addr = callback(dns_hostname->str);
+        }
+    }
+    if (ip_addr) {
+    // Compose DNS answer
+        block_len = DNS_ANSWER_SIZE;
+        dns_reply->data = DAP_REALLOC(dns_reply->data, dns_reply->ptr + block_len);
+        val = 0xc000 || DNS_HEADER_SIZE;                // Link to host name
+        dap_dns_buf_put_uint16(dns_reply, val);
+        val = DNS_RECORD_TYPE_A;
+        dap_dns_buf_put_uint16(dns_reply, val);
+        val = DNS_CLASS_TYPE_IN;
+        dap_dns_buf_put_uint16(dns_reply, val);
+        uint32_t ttl = DNS_TIME_TO_LIVE;
+        dap_dns_buf_put_uint32(dns_reply, ttl);
+        val = 4;                                        // RD len for ipv4
+        dap_dns_buf_put_uint16(dns_reply, val);
+        dap_dns_buf_put_uint32(dns_reply, ip_addr);
+    } else if (flags->rcode == DNS_ERROR_NONE) {
+        flags->rcode = DNS_ERROR_NAME;
+    }
+    if (flags->rcode) {
+        dns_reply->data[7] = 0;                         // No answer section
+    }
+    // Set reply flags
+    dns_reply->data[2] = msg_flags.val >> 8;
+    dns_reply->data[3] = msg_flags.val;
+    // Send DNS reply
+    dap_udp_client_write(client, dns_reply->data, dns_reply->ptr);
+    dap_udp_client_ready_to_write(client, true);
+    dap_string_free(dns_hostname, true);
+cleanup:
+    DAP_DELETE(dns_reply->data);
+    DAP_DELETE(dns_message->data);
+    DAP_DELETE(dns_reply);
+    DAP_DELETE(dns_message);
+    return;
+}
+
+void dap_dns_server_start() {
+    s_dns_server = DAP_NEW(dap_dns_server_t);
+    s_dns_server->hash_table = NULL;
+    s_dns_server->instance = dap_udp_server_listen(DNS_LISTEN_PORT);
+    s_dns_server->instance->client_read_callback = *dap_dns_client_read;
+    s_dns_server->instance->client_write_callback = NULL;
+    s_dns_server->instance->client_new_callback = NULL;
+    s_dns_server->instance->client_delete_callback = NULL;
+    dap_dns_zone_register(&s_root_alias[0], dap_dns_resolve_hostname);  // root resolver
+    pthread_create(&s_dns_server->udp_thread, NULL, (void *)dap_udp_server_loop, s_dns_server->instance);
+}
+
+void dap_dns_server_stop() {
+    dap_dns_zone_hash_t *current_zone, *tmp;
+    HASH_ITER(hh, s_dns_server->hash_table, current_zone, tmp) {
+        HASH_DEL(s_dns_server->hash_table, current_zone);
+        DAP_DELETE(current_zone->zone);
+        DAP_DELETE(current_zone);
+    }
+    // TODO add code to stop udp_thread
+    dap_udp_server_delete(s_dns_server->instance);
+}
diff --git a/dap_dns_server.h b/dap_dns_server.h
new file mode 100755
index 0000000000000000000000000000000000000000..6743732de06df3cd20c9a62f5d8fa8846ed45352
--- /dev/null
+++ b/dap_dns_server.h
@@ -0,0 +1,125 @@
+/*
+ * Authors:
+ * Roman Khlopkov <roman.khlopkov@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://gitlab.demlabs.net
+ * Copyright  (c) 2017-2020
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#ifdef _WIN32
+#include <pthread.h>
+#endif
+#include "dap_server.h"
+#include "uthash.h"
+
+#define DNS_LISTEN_PORT 53      // UDP
+#define DNS_TIME_TO_LIVE 600    // Seconds
+#define DNS_HEADER_SIZE 12
+#define DNS_ANSWER_SIZE 16
+#define DNS_MAX_HOSTNAME_LEN 255
+#define DNS_MAX_DOMAIN_NAME_LEN 63
+
+typedef enum _dap_dns_query_type_t {
+    DNS_QUERY_TYPE_STANDARD,
+    DNS_QUERY_TYPE_INVERSE,
+    DNS_QUERY_TYPE_STATUS
+} dap_dns_query_type_t;
+
+typedef enum _dap_dns_error_t {
+    DNS_ERROR_NONE,         // No error
+    DNS_ERROR_FORMAT,       // DNS message parsing error
+    DNS_ERROR_FAILURE,      // Internal server error
+    DNS_ERROR_NAME,         // Only for authoritative servers. Name does not exist
+    DNS_ERROR_NOT_SUPPORTED,// This kind of query not implemented
+    DNS_ERROR_REFUSED       // Operation refused
+} dap_dns_error_t;
+
+typedef enum _dap_dns_record_type_t {
+    DNS_RECORD_TYPE_A = 1,  // Host address
+    DNS_RECORD_TYPE_NS,     // Authoritative name server
+    DNS_RECORD_TYPE_MD,     // Mail destination (obsolete, use MX)
+    DNS_RECORD_TYPE_MF,     // Mail forwarder (obsolete, use MX)
+    DNS_RECORD_TYPE_CNAME,  // Canonical name of alias
+    DNS_RECORD_TYPE_SOA,    // Marks a start of a zone of authority
+    DNS_RECORD_TYPE_MB,     // Mailbox domain name (experimental)
+    DNS_RECORD_TYPE_MG,     // Mail group member (experimental)
+    DNS_RECORD_TYPE_MR,     // Mail rename domain name (experimental)
+    DNS_RECORD_TYPE_NULL,   // NULL resource record (experimental)
+    DNS_RECORD_TYPE_WKS,    // Well known server description
+    DNS_RECORD_TYPE_PTR,    // Domain name pointer
+    DNS_RECORD_TYPE_HINFO,  // Host information
+    DNS_RECORD_TYPE_MINFO,  // Mail box or list information
+    DNS_RECORD_TYPE_MX,     // Mail exchange
+    DNS_RECORD_TYPE_TXT,    // Text strings
+    DNS_RECORD_TYPE_RP,     // Responsible person
+    DNS_RECORD_TYPE_AXFR = 252, // A request for a transfer of an entire zone - QTYPE only
+    DNS_RECORD_TYPE_MAILB,  // A request for mailbox-related records (MB, MG or MR) - QTYPE only
+    DNS_RECORD_TYPE_MAILA,  // A request for mail agent RRs (obsolete - see MX) - QTYPE only
+    DNS_RECORD_TYPE_ANY     // A request for all records - QTYPE only
+} dap_dns_record_type_t;
+
+typedef enum _dap_dns_class_type_t {
+    DNS_CLASS_TYPE_IN = 1,  // Internet
+    DNS_CLASS_TYPE_CS,      // CSNET (obsolete)
+    DNS_CLASS_TYPE_CH,      // CHAOS
+    DNS_CLASS_TYPE_HS,      // Hesiod [Dyer 87]
+    DNS_CLASS_TYPE_ANY = 255    // Any class
+} dap_dns_class_type_t;
+
+typedef struct _dap_dns_message_flags_bits_t {
+    int rcode : 4;          // response code, answer only: 0 - no error, 1 - format error, 2 - server failure, 3 - name error, 4 - not supported, 5 - refused
+    int z : 3;              // reserved, must be zero
+    int ra : 1;             // 1 - recursion available (answer only)
+    int rd : 1;             // 1 - recursion desired (query set, copied to answer)
+    int tc : 1;             // 1 - message truncated
+    int aa : 1;             // 1 - authoritative answer (answer only)
+    int opcode : 4;         // type of query, copied to answer: 0 - standard, 1 - inverse, 2 - status, 3-15 - reserved
+    int qr : 1;             // 0 - query, 1 - response
+} dap_dns_message_flags_bits_t;
+
+typedef uint32_t (*dap_dns_zone_callback_t) (char *hostname); // Callback for DNS zone operations
+
+typedef union _dap_dns_message_flags_t {
+    dap_dns_message_flags_bits_t flags;
+    int val;
+} dap_dns_message_flags_t;
+
+typedef struct _dap_dns_buf_t {
+    char *data;
+    uint32_t ptr;
+} dap_dns_buf_t;
+
+typedef struct _dap_dns_zone_hash_t {
+    char *zone;
+    dap_dns_zone_callback_t callback;
+    UT_hash_handle hh;
+} dap_dns_zone_hash_t;
+
+typedef struct _dap_dns_server_t {
+    dap_server_t *instance;
+    pthread_t udp_thread;
+    dap_dns_zone_hash_t *hash_table;
+} dap_dns_server_t;
+
+void dap_dns_server_start();
+void dap_dns_server_stop();
+int dap_dns_zone_register(char *zone, dap_dns_zone_callback_t callback);
+int dap_dns_zone_unregister(char *zone);
diff --git a/dap_udp_client.c b/dap_udp_client.c
index 8e72f5682d0a6902a41f2a7c758d7f3119fd0c78..ab13f7de654ba89a6184c4c38eb71a4e7a3c9d28 100755
--- a/dap_udp_client.c
+++ b/dap_udp_client.c
@@ -60,6 +60,7 @@
  * @return 64 bit Key
  */
 #define get_key( host, key ) (((uint64_t)host << 32) + (uint64_t)port)
+extern bool sb_payload_ready;
 
 /**
  * @brief udp_client_create Create new client and add it to hashmap
@@ -72,7 +73,7 @@
 dap_client_remote_t *dap_udp_client_create( dap_server_t *dap_srv, EPOLL_HANDLE efd, unsigned long host, unsigned short port )
 {
   dap_udp_server_t *udp_server = DAP_UDP_SERVER( dap_srv );
-  log_it( L_DEBUG, "Client structure create" );
+  log_it( L_DEBUG, "Client structure create with host = %x, port = %d", host, port );
 
   dap_udp_client_t *inh = DAP_NEW_Z( dap_udp_client_t );
   inh->host_key = get_key( host, port );
@@ -112,11 +113,11 @@ dap_client_remote_t *dap_udp_client_create( dap_server_t *dap_srv, EPOLL_HANDLE
  * @param host Variable for host address
  * @param host Variable for port
  */
-void dap_udp_client_get_address( dap_client_remote_t *client, unsigned int* host, unsigned short* port ) 
+void dap_udp_client_get_address( dap_client_remote_t *client, unsigned int* host, unsigned short* port )
 {
   dap_udp_client_t* udp_client = DAP_UDP_CLIENT( client );
   *host = udp_client->host_key >> 32;
-  *port = (udp_client->host_key <<32) - *host;
+  *port = udp_client->host_key;
 }
 
 /**
@@ -134,7 +135,7 @@ dap_client_remote_t *dap_udp_client_find( dap_server_t *dap_srv, unsigned long h
   uint64_t token = get_key( host, port );
 
   pthread_mutex_lock( &udp_server->mutex_on_list );
-  HASH_FIND_INT( udp_server->hclients, &token, inh );    
+  HASH_FIND_INT( udp_server->hclients, &token, inh );
   pthread_mutex_unlock( &udp_server->mutex_on_list );
 
   if( inh == NULL )
@@ -187,14 +188,18 @@ void dap_udp_client_ready_to_write( dap_client_remote_t *sc, bool is_ready )
     sc->flags |= DAP_SOCK_READY_TO_WRITE;
   else
     sc->flags ^= DAP_SOCK_READY_TO_WRITE;
-
   int events = EPOLLERR;
 
   if( sc->flags & DAP_SOCK_READY_TO_READ )
     events |= EPOLLIN;
 
   if( sc->flags & DAP_SOCK_READY_TO_WRITE )
-    events |= EPOLLOUT;
+  {
+    dap_udp_server_t *udp_server = DAP_UDP_SERVER(sc->server);
+    pthread_mutex_lock(&udp_server->mutex_on_list);
+    sb_payload_ready = true;
+    pthread_mutex_unlock(&udp_server->mutex_on_list );
+  }
 
   sc->pevent.events = events;
 
diff --git a/dap_udp_server.c b/dap_udp_server.c
index 49ed82a21b40fac069a22b8fa448f9ec65c2024e..f6f8e02bd5b06160a32ac85690b7dabd5a9945f8 100755
--- a/dap_udp_server.c
+++ b/dap_udp_server.c
@@ -59,12 +59,11 @@
 #define BUFSIZE 1024
 
 char buf[ BUFSIZE ]; /* message buf */
-
+bool sb_payload_ready;
 //struct ev_io w_read;
 //struct ev_io w_write;
 
 EPOLL_HANDLE efd_read  = (EPOLL_HANDLE)-1;
-EPOLL_HANDLE efd_write = (EPOLL_HANDLE)-1;
 
 //static void write_cb( EPOLL_HANDLE efd, int revents );
 
@@ -72,7 +71,7 @@ int check_close( dap_client_remote_t *client );
 
 /**
  */
-void error( char *msg ) {
+static void error( char *msg ) {
 
   perror( msg );
   exit( 1 );
@@ -126,7 +125,7 @@ void dap_udp_server_delete( dap_server_t *sh )
 /**
  * @brief dap_udp_server_listen Create and bind server structure
  * @param port Binding port
- * @return Server instance 
+ * @return Server instance
  */
 dap_server_t *dap_udp_server_listen( uint16_t port ) {
 
@@ -155,7 +154,7 @@ dap_server_t *dap_udp_server_listen( uint16_t port ) {
     dap_udp_server_delete( sh );
     return NULL;
   }
-
+  log_it(L_INFO, "UDP server listening port 0.0.0.0:%d", port);
   pthread_mutex_init( &DAP_UDP_SERVER(sh)->mutex_on_list, NULL );
   pthread_mutex_init( &DAP_UDP_SERVER(sh)->mutex_on_hash, NULL );
 
@@ -188,14 +187,15 @@ static void write_cb( EPOLL_HANDLE efd, int revents, dap_server_t *sh )
 
       if ( client->buf_out_size > 0 ) {
 
-        //log_it(L_INFO,"write_cb_client");
 
+
+        struct sockaddr_in addr;
+        addr.sin_family = AF_INET;
+        dap_udp_client_get_address( client, (unsigned int *)&addr.sin_addr.s_addr, &addr.sin_port );
+        //log_it(L_INFO,"write_cb_client host = %x, port = %d, socket = %x", addr.sin_addr.s_addr, addr.sin_port, sh->socket_listener);
         for( size_t total_sent = 0; total_sent < client->buf_out_size; ) {
-          struct sockaddr_in addr;
-          addr.sin_family = AF_INET;
-          dap_udp_client_get_address( client, (unsigned int *)&addr.sin_addr.s_addr, &addr.sin_port );
 
-          int bytes_sent = sendto( sh->socket_listener, client->buf_out + total_sent, 
+          int bytes_sent = sendto( sh->socket_listener, client->buf_out + total_sent,
                         client->buf_out_size - total_sent, 0, (struct sockaddr*) &addr, sizeof(addr) );
 
           if ( bytes_sent < 0 ) {
@@ -206,6 +206,8 @@ static void write_cb( EPOLL_HANDLE efd, int revents, dap_server_t *sh )
         }
         client->buf_out_size = 0;
         memset( client->buf_out, 0, DAP_CLIENT_REMOTE_BUF + 1 );
+        client->flags &= ~DAP_SOCK_READY_TO_WRITE;
+        sb_payload_ready = false;
       }
       LL_DELETE( udp->waiting_clients, udp_client );
     }
@@ -302,7 +304,7 @@ static void read_cb( EPOLL_HANDLE efd, int revents, dap_server_t *sh )
 
             if ( sh->client_read_callback )
                 sh->client_read_callback( client, NULL );
-                
+
             bytes_processed += bytes_to_transfer;
             bytes_recieved -= bytes_to_transfer;
         }
@@ -325,66 +327,21 @@ static void read_cb( EPOLL_HANDLE efd, int revents, dap_server_t *sh )
     }
 }
 
-/**
- * @brief dap_udp_client_loop Create client listening event loop
- */
-void *dap_udp_client_loop( void *arg )
-{
-  dap_server_t *d_server = (dap_server_t *)arg;
-  struct epoll_event  pev;
-  struct epoll_event  events[ 16 ];
-
-  pev.events = EPOLLOUT | EPOLLERR;
-  pev.data.fd = d_server->socket_listener;
-
-  if ( epoll_ctl( efd_write, EPOLL_CTL_ADD, d_server->socket_listener, &pev) != 0 ) {
-    log_it( L_ERROR, "epoll_ctl failed 001" );
-    return NULL;
-  }
-
-  log_it( L_NOTICE, "Start client write thread" );
-
-  do {
-
-    int32_t n = epoll_wait( efd_write, events, 16, -1 );
-
-    if ( n == -1 )
-      break;
-
-    for ( int32_t i = 0; i < n; ++ i ) {
-
-      if ( events[i].events & EPOLLOUT ) {
-        write_cb( efd_write, events[i].events, d_server );
-      }
-      else if( events[i].events & EPOLLERR ) {
-        log_it( L_ERROR, "Server socket error event" );
-        break;
-      }
-    }
-
-  } while( 1 );
-
-  return NULL;
-}
-
-
 /**
  * @brief dap_udp_server_loop Start server event loop
  * @param sh Server instance
  */
 void dap_udp_server_loop( dap_server_t *d_server )
 {
-  EPOLL_HANDLE efd_read  = epoll_create1( 0 );
-  EPOLL_HANDLE efd_write = epoll_create1( 0 );
-  pthread_t thread;
+  efd_read  = epoll_create1( 0 );
 
-  if ( (intptr_t)efd_read == -1 || (intptr_t)efd_write == -1 ) {
+  if ( (intptr_t)efd_read == -1 ) {
 
     log_it( L_ERROR, "epoll_create1 failed" );
-    goto error;
+    goto udp_error;
   }
 
-  pthread_create( &thread, NULL, dap_udp_client_loop, d_server );
+  sb_payload_ready = false;
 
   struct epoll_event  pev;
   struct epoll_event  events[ 16 ];
@@ -394,7 +351,7 @@ void dap_udp_server_loop( dap_server_t *d_server )
 
   if ( epoll_ctl( efd_read, EPOLL_CTL_ADD, d_server->socket_listener, &pev) != 0 ) {
     log_it( L_ERROR, "epoll_ctl failed 000" );
-    goto error;
+    goto udp_error;
   }
 
   while( 1 ) {
@@ -415,21 +372,25 @@ void dap_udp_server_loop( dap_server_t *d_server )
       if ( events[i].events & EPOLLIN ) {
         read_cb( efd_read, events[i].events, d_server );
       }
-      else if( events[i].events & EPOLLERR ) {
+      if ( events[i].events & EPOLLOUT) {
+        // Do nothing. It always true until socket eturn EAGAIN
+      }
+      if (sb_payload_ready) {
+        write_cb( efd_read, events[i].events, d_server );
+      }
+      if( events[i].events & EPOLLERR ) {
         log_it( L_ERROR, "Server socket error event" );
-        goto error;
+        goto udp_error;
       }
     }
 
   }
 
-error:
+udp_error:
 
   #ifndef _WIN32
     if ( efd_read != -1 )
       close( efd_read );
-    if ( efd_write != -1 )
-      close( efd_write );
   #else
     if ( efd_read != INVALID_HANDLE_VALUE )
       epoll_close( efd_read );