英文:
Why I keep getting timeout with gen_tcp in Elixir?
问题
我正在尝试实现一个简单的服务器(使用Go语言)/客户端(使用Elixir语言)。
然而,在我的请求到达服务器之前,我一直在Elixir端遇到超时问题。
服务器
package main
import (
"fmt"
"net"
"net/http"
"os"
)
func main() {
socket := "/tmp/ipc_test.sock"
os.Remove(socket)
listener, err := net.Listen("unix", socket)
if err != nil {
fmt.Printf("Error listening on unix socket: %s\n", err)
return
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
param := r.URL.Query().Get("param")
fmt.Printf("%s", param)
fmt.Fprint(w, param)
})
fmt.Printf("Server listening on %s\n", socket)
http.Serve(listener, nil)
}
客户端
defmodule IpcTest do
def make_request(socket, request) do
:gen_tcp.send(socket, request)
{:ok, response} = :gen_tcp.recv(socket, 0)
:gen_tcp.close(socket)
response
end
def print_responses(responses) do
responses
|> Enum.each(&IO.puts/1)
end
def run(num_requests) do
1..num_requests
|> Enum.map(fn i ->
request = "/?param=#{i}"
Task.async(fn ->
{:ok, socket} =
:gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 0, [:binary, packet: 4, active: false])
make_request(socket, request)
end)
end)
|> Enum.map(&Task.await/1)
end
end
> go run src/main.go
Server listening on /tmp/js_runner.sock
如你所见,服务器似乎运行正常。
然而,在我的客户端中,我遇到了以下错误。
> iex -S mix
> IpcTest.run 1
** (exit) exited in: Task.await(%Task{mfa: {:erlang, :apply, 2}, owner: #PID<0.237.0>, pid: #PID<0.263.0>, ref: #Reference<0.3740412160.2895708166.190144>}, 5000)
** (EXIT) time out
(elixir 1.14.2) lib/task.ex:830: Task.await/2
(elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
iex:2: (file)
英文:
I am trying to implement a simple Server (in Go) / Client (in Elixir).
However, I keep getting timeouts on Elixir side before my request reaches the server.
Server
package main
import (
"fmt"
"net"
"net/http"
"os"
)
func main() {
socket := "/tmp/ipc_test.sock"
os.Remove(socket)
listener, err := net.Listen("unix", socket)
if err != nil {
fmt.Printf("Error listening on unix socket: %s\n", err)
return
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
param := r.URL.Query().Get("param")
fmt.Printf("%s", param)
fmt.Fprint(w, param)
})
fmt.Printf("Server listening on %s\n", socket)
http.Serve(listener, nil)
}
Client
defmodule IpcTest do
def make_request(socket, request) do
:gen_tcp.send(socket, request)
{:ok, response} = :gen_tcp.recv(socket, 0)
:gen_tcp.close(socket)
response
end
def print_responses(responses) do
responses
|> Enum.each(&IO.puts/1)
end
def run(num_requests) do
1..num_requests
|> Enum.map(fn i ->
request = "/?param=#{i}"
Task.async(fn ->
{:ok, socket} =
:gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 0, [:binary, packet: 4, active: false])
make_request(socket, request)
end)
end)
|> Enum.map(&Task.await/1)
end
end
> go run src/main.go
Server listening on /tmp/js_runner.sock
As you can see, the server seems to be running fine.
However, I am getting the following error in my client.
> iex -S mix
> IpcTest.run 1
** (exit) exited in: Task.await(%Task{mfa: {:erlang, :apply, 2}, owner: #PID<0.237.0>, pid: #PID<0.263.0>, ref: #Reference<0.3740412160.2895708166.190144>}, 5000)
** (EXIT) time out
(elixir 1.14.2) lib/task.ex:830: Task.await/2
(elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
iex:2: (file)
答案1
得分: 0
默认情况下,:gen_tcp.connect/3函数的超时值为5000毫秒(5秒)。将其增加到10秒:
{:ok, socket} = :gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 10000, [])
但你也可以尝试以下命令:
socat - UNIX-CONNECT:/tmp/ipc_test.sock
或者参考这个链接中的其他方法:https://unix.stackexchange.com/questions/26715/how-can-i-communicate-with-a-unix-domain-socket-via-the-shell-on-debian-squeeze
以查看该套接字文件是否对任何正常的Linux命令行工具有效。
英文:
By default, the :gen_tcp.connect/3 function has a timeout value of 5000 milliseconds (5 seconds). Increase to 10:
{:ok, socket} = :gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 10000, [:
But you should also try stuff like
socat - UNIX-CONNECT:/tmp/ipc_test.sock
Or the other stuff in: https://unix.stackexchange.com/questions/26715/how-can-i-communicate-with-a-unix-domain-socket-via-the-shell-on-debian-squeeze
to see if the socket file is even working for ANY normal linux cli tool.
答案2
得分: 0
搞定了!我的问题实际上出在基于Go的服务器中。
以下是更新后的代码,如果有人感兴趣的话:
package main
import (
"io"
"log"
"net"
"os"
)
const SockAddr = "/tmp/ipc_test.sock"
func echoServer(c net.Conn) {
log.Printf("客户端已连接 [%s]", c.RemoteAddr().Network())
io.Copy(c, c)
c.Close()
}
func main() {
if err := os.RemoveAll(SockAddr); err != nil {
log.Fatal(err)
}
l, err := net.Listen("unix", SockAddr)
if err != nil {
log.Fatal("监听错误:", err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("接受错误:", err)
}
go echoServer(conn)
}
}
参考链接:https://eli.thegreenplace.net/2019/unix-domain-sockets-in-go
英文:
Figured it out! My issue was actually in the Go based server.
Here is the updated code if anyone is interested in:
package main
import (
"io"
"log"
"net"
"os"
)
const SockAddr = "/tmp/ipc_test.sock"
func echoServer(c net.Conn) {
log.Printf("Client connected [%s]", c.RemoteAddr().Network())
io.Copy(c, c)
c.Close()
}
func main() {
if err := os.RemoveAll(SockAddr); err != nil {
log.Fatal(err)
}
l, err := net.Listen("unix", SockAddr)
if err != nil {
log.Fatal("listen error:", err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
go echoServer(conn)
}
}
Reference: https://eli.thegreenplace.net/2019/unix-domain-sockets-in-go
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论