diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua
index c074fe063e7e4fa553a301be76de7b107b982619..3084d5e45cbc76a6a30d0fb525a79164682df2da 100644
--- a/src/lua/bsdsocket.lua
+++ b/src/lua/bsdsocket.lua
@@ -933,22 +933,114 @@ local function tcp_connect(host, port, timeout)
     return nil
 end
 
-socket_mt = {
-        __index     = socket_methods,
-        __tostring  = function(self)
-            local save_errno = self._errno
-            local name = sprintf("fd %d", self.fh)
-            local aka = self:name()
-            if aka ~= nil then
-                name = sprintf("%s, aka %s:%s", name, aka.host, aka.port)
+local function tcp_server_remote(list, prepare, handler)
+    local slist = {}
+
+    -- bind/create sockets
+    for _, addr in pairs(list) do
+        local s = create_socket(addr.family, addr.type, addr.protocol)
+
+        local ok = false
+        if s ~= nil then
+            if s:bind(addr.host, addr.port) then
+                local prepared, backlog = pcall(prepare, s)
+                if prepared and s:listen(backlog) then
+                    ok = true
+                end
+            end
+        end
+
+        -- errors
+        if not ok then
+            if s ~= nil then
+                s:close()
             end
-            local peer = self:peer(self)
-            if peer ~= nil then
-                name = sprintf("%s, peer %s:%s", name, peer.host, peer.port)
+            local save_errno = boxerrno()
+            for _, s in pairs(slist) do
+                s:close()
             end
-            self._errno = save_errno
-            return name
+            boxerrno(save_errno)
+            return nil
+        end
+
+        table.insert(slist, s)
+    end
+    
+    local server = { s = slist }
+
+    server.stop = function()
+        if #server.s == 0 then
+            return false
         end
+        for _, s in pairs(server.s) do
+            s:close()
+        end
+        server.s = {}
+        return true
+    end
+
+    for _, s in pairs(server.s) do
+        fiber.create(function(s)
+            fiber.name(sprintf("listen_fd=%d",s.fh))
+
+            while s:readable() do
+                
+                local sc = s:accept()
+
+                if sc == nil then
+                    break
+                end
+
+                fiber.create(function(sc)
+                    pcall(handler, sc)
+                    sc:close()
+                end, sc)
+            end
+        end, s)
+    end
+
+    return server
+end
+
+local function tcp_server(host, port, prepare, handler, timeout)
+    if handler == nil then
+        handler = prepare
+        prepare = function() end
+    end
+
+    if type(prepare) ~= 'function' or type(handler) ~= 'function' then
+        error("Usage: socket.tcp_server(host, port[, prepare], handler)")
+    end
+
+    if host == 'unix/' then
+        return tcp_server_remote({{host = host, port = port, protocol = 0,
+            family = 'PF_UNIX', type = 'SOCK_STREAM' }}, prepare, handler)
+    end
+
+    local dns = getaddrinfo(host, port, timeout, { type = 'SOCK_STREAM',
+        protocol = 'tcp' })
+    if dns == nil then
+        return nil
+    end
+    return tcp_server_remote(dns, prepare, handler)
+end
+
+socket_mt   = {
+    __index     = socket_methods,
+    __tostring  = function(self)
+        local save_errno = self._errno
+        local name = sprintf("fd %d", self.fh)
+        local aka = self:name()
+        if aka ~= nil then
+            name = sprintf("%s, aka %s:%s", name, aka.host, aka.port)
+        end
+        local peer = self:peer(self)
+        if peer ~= nil then
+            name = sprintf("%s, peer %s:%s", name, peer.host, peer.port)
+        end
+        self._errno = save_errno
+        return name
+    end
 }
 
 if package.loaded.socket == nil then
@@ -956,7 +1048,7 @@ if package.loaded.socket == nil then
 end
 
 package.loaded.socket.tcp_connect = tcp_connect
--- package.loaded.socket.tcp_server = tcp_server
+package.loaded.socket.tcp_server = tcp_server
 
 setmetatable(package.loaded.socket, {
     __call = function(self, ...) return create_socket(...) end,
diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result
index 419e84a62149b4b4c1ae7f8184eda548e893ed6d..a1443a1f7c75967785d3aede83d33a1525b9dead 100644
--- a/test/box/bsdsocket.result
+++ b/test/box/bsdsocket.result
@@ -1178,3 +1178,32 @@ os.remove(path)
 ---
 - true
 ...
+server = socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end)
+---
+...
+server ~= nil
+---
+- true
+...
+fiber.sleep(.5)
+---
+...
+client = socket.tcp_connect('unix/', path)
+---
+...
+client ~= nil
+---
+- true
+...
+client:read(123)
+---
+- Hello, world
+...
+server:stop()
+---
+- true
+...
+os.remove(path)
+---
+- true
+...
diff --git a/test/box/bsdsocket.test.lua b/test/box/bsdsocket.test.lua
index ef953214eba84277d6fce1f437f0041afc821004..1d51a54a7a2b01499f2bde878b73478597871901 100644
--- a/test/box/bsdsocket.test.lua
+++ b/test/box/bsdsocket.test.lua
@@ -387,3 +387,20 @@ c:close()
 s:close()
 
 os.remove(path)
+
+
+server = socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end)
+
+server ~= nil
+
+fiber.sleep(.5)
+
+client = socket.tcp_connect('unix/', path)
+
+client ~= nil
+
+client:read(123)
+
+server:stop()
+
+os.remove(path)