1

As I understand, reading net/rpc package documentation here https://pkg.go.dev/net/[email protected] that every time a client makes an rpc call to the server, a new connection established. How can achieve that each new client opens a new connection, keeps it alive and invokes RPC methods using only TPC, i.e. not using HTTP?

5
  • Why do you think that each call invokes a new connection? Commented Dec 12, 2021 at 21:24
  • @JimB after reading documentation. I haven't seen anything about keeping connections etc. Commented Dec 12, 2021 at 21:31
  • "A client wishing to use the service establishes a connection and then invokes NewClient on the connection", a client is based on a single connection Commented Dec 12, 2021 at 21:34
  • yes, and what happens after a remote call? I guess, connection auto-closed. I need to have a possibility to keep a list of all open connections on the server Commented Dec 12, 2021 at 21:40
  • 3
    The connection is retained by the client until the application explicitly closes the client. The application must close the client to prevent leaks. There's no need to keep a list of all open clients to a server — just reuse a single client to the server. Commented Dec 12, 2021 at 21:44

1 Answer 1

6

If you make a new client with any of the standard library methods:

client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
    log.Fatal("dialing:", err)
}

Underneath the hood it will call net.Dial, resulting in a single connection that is associated with the rpc.Client:

conn, err := net.Dial(network, address)

You can see NewClient taking a single connection when it's instantiated here: https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/net/rpc/client.go;l=193-197;drc=refs%2Ftags%2Fgo1.17.5;bpv=1;bpt=1

Any calls to Client.Call on that client will write and read to that underlying connection without spawning a new connection.

So as long as you instantiate your client one time and then make all of your rpc calls to that same client you'll always use a single connection. If that connection ever is severed the client will not longer be usable.

rpc.Client is also threadsafe, so you can safely create it and use it all over the place without having to make new connections .


Answering your comment. If you wanted to run an rpc server and keep track of connections you could do this:

l, e := net.Listen("tcp", ":1234")
if e != nil {
    log.Fatal("listen error:", e)
}
server := rpc.NewServer()
for {
    conn, err := l.Accept()
    if err != nil {
        panic(err) // replace with log message?
    }
    // Do something with `conn`
    go func() {
        server.ServeConn(conn)
        // The server has stopped serving this connection, you can remove it.
    }()
}

And then do something with each connection as it came in, and remove it when it's done processing.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, what will be a server code to keep a list of all connections and access them (by server) if needed? I saw this code, does it mean that rpc.ServeConn call keeps this connection active inside? ..and if I want to have access to all connections, I need to save a link to conn somewhere before that call? for { conn, err := listener.Accept() if err != nil { continue } rpc.ServeConn(conn) } here ipfs.io/ipfs/QmfYeDhGH9bZzihBUDEQbCbTc5k5FZKURMUoUvfmc27BwL/rpc/…
edit: oh I see what you're saying, let me add how to do that to the answer
answered above ^

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.