Simple RPC Call in Go

341 views
Skip to first unread message

kevi...@free.fr

unread,
Dec 4, 2013, 12:27:31 PM12/4/13
to golan...@googlegroups.com
I am trying to get a minimal application working using RPC calls in Go. I am heavily borrowing from the online example, as you can see from my code:

server.go:
    package main
   
    import (
        [...]
    )
   
    type InfoDumper int
   
    func (s *InfoDumper) Dump(request string, reply *string) error {
   
        fmt.Println("Woooh imma deliverin stuff\n")
   
        current_time := time.Now()
   
        h:= sha1.New()
        var barray []byte
        copy(barray, request)
        hash_rq := h.Sum(barray)
   
        *reply = request + "\n" + current_time.Format(time.ANSIC) + "\n"  + string(hash_rq) + "\n"
        return nil
    }
   
   
    func main() {
   
        server := new(InfoDumper)
   
        rpc.Register(server)
        rpc.HandleHTTP()
   
        l, e := net.Listen("tcp", "127.0.0.1:40000")
        if e != nil {
            fmt.Println(e)
        }
   
        http.Serve(l, nil)
   
    }

client.go:
    package main
   
    import (
        [...]
    )
   
    func main() {
   
        client, e := rpc.Dial("tcp", "127.0.0.1:40000")
   
        if e!=nil {
            fmt.Println(e)
        }    else {
            fmt.Println("wooh server is ok")
        }
   
        in:= bufio.NewReader(os.Stdin)
   
        for {
   
            line, _, _ := in.ReadLine()
            request := string(line)
            var reply string
   
            e = client.Call("InfoDumper.Dump", request, &reply)
   
            if (e!=nil) {
                fmt.Println("omg error!", e)
            }
   
            fmt.Println(reply)
        }
   
    }

The only difference I can see is that I wrote http.Serve(l, nil) instead of go http.Serve(l, nil) ; this is because writing with go makes my server terminate right away.
InfoDump is supposed to reply with a copy of whatever was sent, the time and hash of the request.

This is what's happening right now:

  • I run server.go in a terminal
  • I run client.go in another terminal, after a second or so "wooh server is ok" is printed
  • I type something and hit Enter on the client's side
  • either nothing happens, or "rpc: client protocol error: unexpected EOF" is printed on the client's side
  • if nothing happened, terminating the server (ie hitting Control-C) makes the client print the error above

In either case, "Woooh imma deliverin stuff" is never displayed on the server's side...

This was done during class as a preliminary step to get acquainted with RPC in Go before going on to more serious exercises ; all the other students managed to get this step working, looked at this code and couldn't see the difference with theirs.

Does anyone see anything wrong with this code?

Kyle Lemons

unread,
Dec 4, 2013, 1:49:45 PM12/4/13
to kevi...@free.fr, golang-nuts
On Wed, Dec 4, 2013 at 9:27 AM, <kevi...@free.fr> wrote:
I am trying to get a minimal application working using RPC calls in Go. I am heavily borrowing from the online example, as you can see from my code:

Did you try with the actual example to see if it works?  Also, gofmt your code.
 
server.go:
    package main
   
    import (
        [...]
    )
   
    type InfoDumper int
   
    func (s *InfoDumper) Dump(request string, reply *string) error {
   
        fmt.Println("Woooh imma deliverin stuff\n")
   
        current_time := time.Now()

(camelCase is the norm in Go)
 
        h:= sha1.New()
        var barray []byte
        copy(barray, request)
        hash_rq := h.Sum(barray)

This is not correct.  I think you're looking for `io.WriteString(h, request); hashed := h.Sum(nil)`
 
        *reply = request + "\n" + current_time.Format(time.ANSIC) + "\n"  + string(hash_rq) + "\n"
        return nil
    }
   
   
    func main() {
   
        server := new(InfoDumper)
   
        rpc.Register(server)
        rpc.HandleHTTP()

This also registers /debug/rpc, have you looked at that?
 
        l, e := net.Listen("tcp", "127.0.0.1:40000")
        if e != nil {
            fmt.Println(e)
        }
   
        http.Serve(l, nil)

I would go with `if err := http.ListenAndServe(...); err != nil { log.Fatalf("ListenAndServe: %s", err) }` or something.
 
    }

client.go:
    package main
   
    import (
        [...]
    )
   
    func main() {
   
        client, e := rpc.Dial("tcp", "127.0.0.1:40000")

DialHTTP
 
        if e!=nil {
            fmt.Println(e)
You should os.Exit(1) or log.Fatal here.
 
        }    else {
            fmt.Println("wooh server is ok")
        }
   
        in:= bufio.NewReader(os.Stdin)

(bufio.Scanner is a better way to read lines)
 
        for {
   
            line, _, _ := in.ReadLine()
            request := string(line)
            var reply string
   
            e = client.Call("InfoDumper.Dump", request, &reply)
   
            if (e!=nil) {

(no parenthesis)
 
                fmt.Println("omg error!", e)

again, fail out.
 
            }
   
            fmt.Println(reply)
        }
   
    }

The only difference I can see is that I wrote http.Serve(l, nil) instead of go http.Serve(l, nil) ; this is because writing with go makes my server terminate right away.
InfoDump is supposed to reply with a copy of whatever was sent, the time and hash of the request.

This is what's happening right now:

  • I run server.go in a terminal
  • I run client.go in another terminal, after a second or so "wooh server is ok" is printed
  • I type something and hit Enter on the client's side
  • either nothing happens, or "rpc: client protocol error: unexpected EOF" is printed on the client's side
  • if nothing happened, terminating the server (ie hitting Control-C) makes the client print the error above

In either case, "Woooh imma deliverin stuff" is never displayed on the server's side...

This was done during class as a preliminary step to get acquainted with RPC in Go before going on to more serious exercises ; all the other students managed to get this step working, looked at this code and couldn't see the difference with theirs.

Does anyone see anything wrong with this code?

Your problem is probably using HTTP on the server but raw RPC on the client (change to DialHTTP), but you have a lot of tools at your disposal to debug both this and the rest: /debug/rpc (for some rpc-side debugging; I forget what's in there), SIGQUIT (aka ^/, which dumps goroutines), gdb (steps through your code), etc.
 

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

kevi...@free.fr

unread,
Dec 4, 2013, 4:46:42 PM12/4/13
to golan...@googlegroups.com, kevi...@free.fr
Ow, and here I thought I was doing things exactly like the example showed. So much for pretending I looked at my code thoroughly (well, I did look at 1/ the code 2/ the example 3/ the Internet for longer than it should have taken).
Changed Dial to DialHTTP and it works as I meant it to.

Thanks for the pointers to the debugging tools! I'll admit that so far with Go, I had not coded anything so complex that it required debugging, but that should have been a reflex anyway. As for my way of getting a hash value or reading user input, I figured they wouldn't be optimal (I mostly rushed through the first functions I saw in the online package documentation), I was simply impatient to get something working. So thanks for the corrections on that too!
And thanks for the various comments and best practices (gofmt, camelCase, failing gracefully), they have been duly noted!

I also posted this question on StackOverflow, if you're interested in posting your answer there.
Reply all
Reply to author
Forward
0 new messages