I'm working in Go and a bit new to working with unix sockets. Tried searching for a similar question, but couldn't find anything, so apologies if this has been answered before.
I want to use unix sockets to simulate a cluster of machines for testing. I'm testing my implementation of Raft, so I want to register the multiple objects of the same type (a hefty struct) on different unix sockets. But looking at a simple example I wrote, the effect seems to be not what I want: dialing different sockets for the same exported method seems to collapse on a single port:
package main
import (
"net"
"fmt"
"net/rpc"
"log"
"sync"
)
type Server struct {
name string
}
type SpeakArgs struct {
}
type SpeakReply struct {
Success bool
}
type AddArgs struct {
A, B int
}
type AddReply struct {
Answer int
Success bool
}
func (s *Server) Add(args *AddArgs, reply *AddReply) error {
reply.Answer = args.A + args.B
reply.Success = true
return nil
}
func (s *Server) Speak(args *SpeakArgs, reply *SpeakReply) error {
fmt.Printf("My name is %v.
", s.name)
reply.Success = true
return nil
}
func main() {
var wgMain, wgRegister, wgCall sync.WaitGroup
wgMain.Add(3)
wgRegister.Add(2)
wgCall.Add(1)
go func() {
server := &Server{name: "RICHARD"}
rpc.Register(server)
la, e := net.Listen("unix", "/tmp/example1.sock")
if e != nil {
log.Fatal("listen error:", e)
}
wgRegister.Done()
go rpc.Accept(la)
wgCall.Wait()
la.Close()
wgMain.Done()
fmt.Println("Server exited.")
}()
go func() {
server := &Server{name: "BENNY"}
rpc.Register(server)
lb, e := net.Listen("unix", "/tmp/example2.sock")
if e != nil {
log.Fatal("listen error:", e)
}
wgRegister.Done()
go rpc.Accept(lb)
wgCall.Wait()
lb.Close()
wgMain.Done()
fmt.Println("Server exited.")
}()
go func() {
wgRegister.Wait()
oneclient, err1 := rpc.Dial("unix", "/tmp/example1.sock")
twoclient, err2 := rpc.Dial("unix", "/tmp/example2.sock")
if err1 != nil {
log.Fatal("listen error:", err1)
}
if err2 != nil {
log.Fatal("listen error:", err2)
}
addArgs := &AddArgs{1, 2}
addReply := &AddReply{}
speakArgs := &SpeakArgs{}
speakReply := SpeakReply{}
oneclient.Call("Server.Add", addArgs, addReply)
oneclient.Call("Server.Speak", speakArgs, speakReply)
twoclient.Call("Server.Speak", speakArgs, speakReply)
fmt.Printf("Added numbers! %v + %v = %v.
", addArgs.A, addArgs.B, addReply.Answer)
wgCall.Done()
oneclient.Close()
twoclient.Close()
wgMain.Done()
fmt.Println("Client exited.")
}()
wgMain.Wait()
}
I expect this to print both RICHARD and BENNY in console when Speak() is called on the different clients, but instead it prints one of them twice, seemingly randomly. Is there something I'm missing here? Example output, this time RICHARD seemed to come out, but sometimes it is BENNY.
My name is RICHARD.
My name is RICHARD.
Added numbers! 1 + 2 = 3.
Client exited.
Server exited.
Server exited.
Edit: Fix thanks to Pavlo Strokov! rpc.Register defaults to DefaultServer
if not specified, so both were getting registered on the same RPC server: correction is to call rpc.NewServer()
and register each server on that one.
package main
import (
"net"
"fmt"
"net/rpc"
"log"
"sync"
)
type Server struct {
name string
}
type SpeakArgs struct {
}
type SpeakReply struct {
Success bool
}
type AddArgs struct {
A, B int
}
type AddReply struct {
Answer int
Success bool
}
func (s *Server) Add(args *AddArgs, reply *AddReply) error {
reply.Answer = args.A + args.B
reply.Success = true
return nil
}
func (s *Server) Speak(args *SpeakArgs, reply *SpeakReply) error {
fmt.Printf("My name is %v.
", s.name)
reply.Success = true
return nil
}
func main() {
var wgMain, wgRegister, wgCall sync.WaitGroup
wgMain.Add(3)
wgRegister.Add(2)
wgCall.Add(1)
go func() {
rpcServer := rpc.NewServer()
server := &Server{name: "RICHARD"}
rpcServer.Register(server)
la, e := net.Listen("unix", "/tmp/example1.sock")
if e != nil {
log.Fatal("listen error:", e)
}
wgRegister.Done()
go rpcServer.Accept(la)
wgCall.Wait()
la.Close()
wgMain.Done()
fmt.Println("Server exited.")
}()
go func() {
rpcServer := rpc.NewServer()
server := &Server{name: "BENNY"}
rpcServer.Register(server)
lb, e := net.Listen("unix", "/tmp/example2.sock")
if e != nil {
log.Fatal("listen error:", e)
}
wgRegister.Done()
go rpcServer.Accept(lb)
wgCall.Wait()
lb.Close()
wgMain.Done()
fmt.Println("Server exited.")
}()
go func() {
wgRegister.Wait()
oneclient, err1 := rpc.Dial("unix", "/tmp/example1.sock")
twoclient, err2 := rpc.Dial("unix", "/tmp/example2.sock")
if err1 != nil {
log.Fatal("listen error:", err1)
}
if err2 != nil {
log.Fatal("listen error:", err2)
}
addArgs := &AddArgs{1, 2}
addReply := &AddReply{}
speakArgs := &SpeakArgs{}
speakReply := &SpeakReply{}
oneclient.Call("Server.Add", addArgs, addReply)
oneclient.Call("Server.Speak", speakArgs, speakReply)
twoclient.Call("Server.Speak", speakArgs, speakReply)
fmt.Printf("Added numbers! %v + %v = %v.
", addArgs.A, addArgs.B, addReply.Answer)
wgCall.Done()
oneclient.Close()
twoclient.Close()
wgMain.Done()
fmt.Println("Client exited.")
}()
wgMain.Wait()
}
which prints
My name is RICHARD.
My name is BENNY.
Added numbers! 1 + 2 = 3.
Client exited.
Server exited.
Server exited.