// NewServer creates a new RPC-over-netchan server. It returns a new // Server instance containing a netchan.Exporter and an rpc.Server which // is listening on a channel within it. It reserves the use of netchan // channels with the prefix "ncrpc". // // If acceptClientRPC is true, the server will accept incoming client // RPC registrations made by Client.Serve. // // Conventionally Register is called on the rpc.Server to export some // server RPC methods, and ListenAndServe is then called on the // netchan.Exporter to listen on the network. func NewServer(acceptClientRPC bool) (*Server, error) { rpcsrv := rpc.NewServer() exp := netchan.NewExporter() nclis, err := ncnet.Listen(exp, "ncrpc.ctl") if err != nil { return nil, err } srv := &Server{ clients: make(map[string]*rpc.Client), Exporter: exp, RPCServer: rpcsrv, } rpcsrv.RegisterName("Ncnet-publisher", publisher{acceptClientRPC, srv}) go func() { rpcsrv.Accept(nclis) nclis.Close() }() return srv, nil }
// Publish is the RPC method that allows a client to publish its own RPC // interface. It is called (remotely) by Client.Serve. func (p publisher) Publish(name *string, clientId *string) error { if !p.acceptClientRPC { return errors.New("client RPC connections not accepted") } srv := p.srv srv.mu.Lock() defer srv.mu.Unlock() if srv.clients[*name] != nil { return errors.New("client name already exists") } *clientId = fmt.Sprintf("ncrpc.client%d", srv.clientid) srv.clientid++ listener, err := ncnet.Listen(srv.Exporter, *clientId) if err != nil { return fmt.Errorf("cannot listen on netchan %q: %v", *clientId, err) } go func() { conn, err := listener.Accept() if err != nil { log.Printf("error on ncnet.Accept(%q): %v", *clientId, err) return } listener.Close() client := rpc.NewClient(conn) err = client.Call("ClientRPC.Ping", &struct{}{}, &struct{}{}) if err != nil { log.Printf("error on init: %v", err) return } srv.mu.Lock() srv.clients[*name] = client srv.mu.Unlock() // when call completes, client has left. client.Call("ClientRPC.Wait", &struct{}{}, &struct{}{}) srv.mu.Lock() delete(srv.clients, *name) srv.mu.Unlock() }() return nil }
func (srv *Server) Publish(name *string, clientId *string) error { srv.mu.Lock() defer srv.mu.Unlock() if srv.clients[*name] != nil { return errors.New("client name already exists") } *clientId = fmt.Sprintf("client%d", srv.clientid) srv.clientid++ listener, err := ncnet.Listen(srv.exp, *clientId) if err != nil { return fmt.Errorf("cannot listen on netchan %q: %v", *clientId, err) } go func() { conn, err := listener.Accept() if err != nil { log.Printf("error on ncnet.Accept(%q): %v", *clientId, err) return } listener.Close() client := rpc.NewClient(conn) err = client.Call("Client.Init", &Void{}, &Void{}) if err != nil { log.Printf("error on init: %v", err) return } srv.mu.Lock() srv.clients[*name] = client srv.mu.Unlock() // when call completes, client has left. client.Call("Client.Wait", &Void{}, &Void{}) srv.mu.Lock() delete(srv.clients, *name) srv.mu.Unlock() }() return nil }
func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "usage: share [options] tcp-addr\n") flag.PrintDefaults() os.Exit(2) } flag.Parse() if flag.NArg() != 1 { flag.Usage() return } addr := flag.Arg(0) if *server { exp := netchan.NewExporter() if err := exp.ListenAndServe("tcp", addr); err != nil { log.Fatal("listen failed: ", err) } listener, err := ncnet.Listen(exp, "ctl") if err != nil { log.Fatal("ncnet listen failed: ", err) } srv := &Server{ exp: exp, clients: make(map[string]*rpc.Client), } rpcsrv := rpc.NewServer() if err := rpcsrv.Register(srv); err != nil { log.Fatal("rpcsrv register failed: ", err) } rpcsrv.Accept(listener) listener.Close() return } imp, err := netchan.Import("tcp", addr) if err != nil { log.Fatal("netchan import failed: ", err) } srvconn, err := ncnet.Dial(imp, "ctl") if err != nil { log.Fatal("ncnet dial failed: ", err) } srv := rpc.NewClient(srvconn) var clientId string if err := srv.Call("Server.Publish", clientName, &clientId); err != nil { log.Fatal("publish failed: %v", err) } clientsrv := rpc.NewServer() if err := clientsrv.Register(Client{}); err != nil { log.Fatal("clientsrv register failed: ", err) } clientconn, err := ncnet.Dial(imp, clientId) if err != nil { log.Fatalf("ncnet dial %q failed: %v", clientId, err) } go clientsrv.ServeConn(clientconn) interact(srv) clientconn.Close() time.Sleep(0.1e9) // wait for close to propagate }