Getting EOF from server as client in Go -
i have go client custom protocol. protocol lz4-compressed json-rpc 4 byte header giving length of compressed json.
func receivemessage(conn net.conn) ([]byte, error) { start := time.now() bodylen := 0 body := make([]byte, 0, 4096) buf := make([]byte, 0, 256) bodylen == 0 || len(body) < bodylen { if len(body) > 4 { header := body[:4] body = body[:4] bodylen = int(unpack(header)) } n, err := conn.read(buf[:]) if err != nil { if err != io.eof { return body, err } } body = append(body, buf[0:n]...) := time.now() if now.sub(start) > time.duration(readtimeout) * time.millisecond { return body, fmt.errorf("timed-out while reading socket.") } time.sleep(time.duration(1) * time.millisecond) } return lz4.decode(nil, body) } the client:
func main() { address := os.args[1] msg := []byte(os.args[2]) fmt.printf("sending %s %s\n", msg, address) conn, err := net.dial(address) if err != nil { fmt.printf("%v\n", err) return } // library call _, err = sendmessage(conn, []byte(msg)) if err != nil { fmt.printf("%v\n", err) return } response, err := receivemessage(conn) conn.close() if err != nil { fmt.printf("%v\n", err) return } fmt.printf("response: %s\n", response) } when call it, no response , times out. (if not explicitly ignore eof, returns there io.eof error.) have library written in python works against same endpoint same payload. see immediately?
[jimb beat me answer here goes anyway.]
the root issue did body = body[:4] when wanted body = body[4:]. former keeps first 4 header bytes while latter tosses 4 header bytes decoded.
here self contained version debug logs works. has of other changes mentioned. (i guessed @ various things didn't include, lz4 package used, timeout, unpack, etc.)
package main import ( "encoding/binary" "errors" "fmt" "io" "log" "net" "time" "github.com/bkaradzic/go-lz4" ) const readtimeout = 30 * time.second // xxx guess func receivemessage(conn net.conn) ([]byte, error) { bodylen := 0 body := make([]byte, 0, 4096) var buf [256]byte conn.setdeadline(time.now().add(readtimeout)) defer conn.setdeadline(time.time{}) // disable deadline bodylen == 0 || len(body) < bodylen { if bodylen == 0 && len(body) >= 4 { bodylen = int(unpack(body[:4])) body = body[4:] if bodylen <= 0 { return nil, errors.new("invalid body length") } log.println("read bodylen:", bodylen) continue } n, err := conn.read(buf[:]) body = append(body, buf[:n]...) log.printf("appended %d bytes, len(body) %d", n, len(body)) // note, checked *after* handing n bytes. // io.reader allowed return data error. if err != nil { if err != io.eof { return nil, err } break } } if len(body) != bodylen { return nil, fmt.errorf("got %d bytes, expected %d", len(body), bodylen) } return lz4.decode(nil, body) } const address = ":5678" var msg = []byte(`{"foo":"bar"}`) func main() { //address := os.args[1] //msg := []byte(os.args[2]) fmt.printf("sending %s %s\n", msg, address) conn, err := net.dial("tcp", address) if err != nil { fmt.printf("%v\n", err) return } // library call _, err = sendmessage(conn, msg) if err != nil { fmt.printf("%v\n", err) return } response, err := receivemessage(conn) conn.close() if err != nil { fmt.printf("%v\n", err) return } fmt.printf("response: %s\n", response) } // guess @ `unpack` func unpack(b []byte) uint32 { return binary.littleendian.uint32(b) } func sendmessage(net.conn, []byte) (int, error) { // stub return 0, nil } func init() { // start simple test server in same process go-routine. ln, err := net.listen("tcp", address) if err != nil { log.fatal(err) } go func() { defer ln.close() { conn, err := ln.accept() if err != nil { log.fatalln("accept:", err) } go serve(conn) } }() } func serve(c net.conn) { defer c.close() // skip readding initial request/message , respond const response = `{"somefield": "someval"}` // (de)compression in go done streaming via // io.reader or io.writer need final length. data, err := lz4.encode(nil, []byte(response)) if err != nil { log.println("lz4 encode:", err) return } log.println("sending len:", len(data)) if err = binary.write(c, binary.littleendian, uint32(len(data))); err != nil { log.println("writing len:", err) return } log.println("sending data") if _, err = c.write(data); err != nil { log.println("writing compressed response:", err) return } log.println("serve done, closing connection") } playground (but not runnable there).
Comments
Post a Comment