I'm trying to write a simple client-server application. It works fine with both sides on the same computer, but it blocks when run on two different computers. The computers are a RaspBerry Pi 2 and a MacBook Pro.
The problem is that the client blocks in binary.Write after a few good runs. Typically, the server that receives the data prints that it has received 63 and the client that sends the data prints that it will be sending 64.
If I ad the deadline to the server, it quits with a time-out. The client also has a deadline but doesn't time out.
I tried replacing the data by a simple byte array or just a single int. It still blocks. But the smaller the data is, the more times it goes right.
If I remove the Printf functions in the client, it also goes well for a longer time.
I also tried replacing the functionality of the server by a simple copy from the socket to STDIO (a tip I found here somewhere) but it still fails to come out of binary.Write.
Any idea?
Here is the complete source. There's some bogus stuff in there (the count loop, for example), but it's all really quite simple. Or am I mistaken?
package main
import (
// "bytes"
"encoding/binary"
"fmt"
"io"
"net"
"os"
"os/exec"
"runtime/trace"
"time"
"unsafe"
)
type TheMessageType struct {
X int32
RestOfMessage [1000]byte
}
func listener(theCounter int, active bool, f *os.File) {
var message TheMessageType
var err error
var conn net.Conn
count := theCounter
for {
// Dial the server
fmt.Printf("LISTENER %d: connecting to socket
", count)
conn, err = net.Dial("tcp", "192.168.1.113:49152")
if err != nil {
fmt.Printf("LISTENER %d: not connected, retrying in 3 seconds
", count)
time.Sleep(3 * time.Second)
} else {
fmt.Printf("LISTENER %d: connected
", count)
break
}
}
defer conn.Close()
// Loop writing to socket
for {
// Set deadline
// conn.SetDeadline(time.Now().Add(2 * time.Second))
// Print some info
fmt.Printf("LISTENER %d: writing from %v to %v
", count, conn.LocalAddr(), conn.RemoteAddr())
// Prepare message to write to socket
message.X = int32(count)
theString := fmt.Sprintf("%d", count)
copy(message.RestOfMessage[:], theString)
// binary write directly to socket
fmt.Printf("LISTENER %d: binary.Write this: %d, %s
", count, message.X, message.RestOfMessage)
f.Sync()
err = binary.Write(conn, binary.LittleEndian, message)
f.Sync()
fmt.Printf("LISTENER %d: written
", count)
// time.Sleep(50 * time.Millisecond)
checkError(err, "LISTENER")
count = count + 1
}
}
func main() {
var myMessage TheMessageType
var ln net.Listener
var conn net.Conn
var err error
var theCount int
var avgspeed, speed float64
var speedlist [10]float64
curspeed := 0
const listenerActive = true
const mainActive = false
f, err := os.Create("theFile.out")
trace.Start(f)
t0 := time.Now()
t1 := time.Now()
transferSize := unsafe.Sizeof(myMessage)
fmt.Printf("MAIN: timestamp %s ; size of transfers is %d
", t0.String(), transferSize)
if mainActive {
fmt.Println("MAIN: listen")
ln, err = net.Listen("tcp", ":49152")
fmt.Println("MAIN: defer")
defer ln.Close()
fmt.Println("MAIN: checkError")
checkError(err, "MAIN")
}
// launch listener
if listenerActive {
go listener(theCount, listenerActive, f)
}
for theCount = 1; ; theCount++ {
if mainActive {
fmt.Println("MAIN: accept")
conn, err = ln.Accept()
checkError(err, "MAIN")
exit := false
for !exit {
// Set deadline
// conn.SetDeadline(time.Now().Add(2 * time.Second))
// Print some info
fmt.Printf("MAIN: reading on %v from %v
", conn.LocalAddr(), conn.RemoteAddr())
// move time
t0 = t1
// read from socket
fmt.Println("MAIN: reader")
f.Sync()
err = binary.Read(conn, binary.LittleEndian, &myMessage)
f.Sync()
if err == io.EOF {
fmt.Println("EOF!")
exit = true
conn.Close()
} else {
checkError(err, "MAIN")
}
// clear screen
c := exec.Command("clear")
c.Stdout = os.Stdout
c.Run()
// print received data
fmt.Printf("MAIN: socket message received: %d, %s
", myMessage.X, myMessage.RestOfMessage)
// calculate speed & average
t1 = time.Now()
tdif := t1.Sub(t0)
speed = float64(transferSize) / tdif.Seconds() / 1000000.0 * 8.0
speedlist[curspeed] = speed
curspeed = curspeed + 1
if curspeed == 10 {
curspeed = 0
}
totalspeed := 0.0
for i := 0; i < 10; i++ {
totalspeed += speedlist[i]
}
avgspeed = totalspeed / 10
// print speed
fmt.Printf("MAIN: avg mbit/s %9.2f ; actual mbit/s: %9.2f
", avgspeed, speed)
// time.Sleep(1 * time.Second)
}
}
}
}
func checkError(err error, name string) {
if err != nil {
fmt.Fprintf(os.Stderr, "%s: Fatal error: %s
", name, err.Error())
// panic(fmt.Sprintf("%v", err.Error()))
os.Exit(1)
}
}
Here's the stacktrace of the client when it's blocked writing:
goroutine 1 [running]:
runtime.throw(0x1927b8, 0x5)
/usr/local/go/src/runtime/panic.go:527 +0x90 fp=0xc82004bd78 sp=0xc82004bd60
runtime.sigpanic()
/usr/local/go/src/runtime/sigpanic_unix.go:27 +0x2ba fp=0xc82004bdc8 sp=0xc82004bd78
main.main()
/Users/ple/dev/go/src/Peter-test/main.go:108 +0x33c fp=0xc82004bf50 sp=0xc82004bdc8
runtime.main()
/usr/local/go/src/runtime/proc.go:111 +0x2b0 fp=0xc82004bfa0 sp=0xc82004bf50
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc82004bfa8 sp=0xc82004bfa0
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1696 +0x1
goroutine 5 [trace reader (blocked)]:
runtime.ReadTrace(0x0, 0x0, 0x0)
/usr/local/go/src/runtime/trace.go:339 +0x207
runtime/trace.Start.func1(0x5a42e0, 0xc82002a020)
/usr/local/go/src/runtime/trace/trace.go:28 +0x18
created by runtime/trace.Start
/usr/local/go/src/runtime/trace/trace.go:34 +0x78
goroutine 6 [runnable]:
reflect.Value.Index(0x1234c0, 0xc8204ac004, 0x51, 0x75, 0x12ac00, 0xc8204ac078, 0x48)
/usr/local/go/src/reflect/value.go:823
encoding/binary.(*encoder).value(0xc8200475a0, 0x1234c0, 0xc8204ac004, 0x51)
/usr/local/go/src/encoding/binary/binary.go:509 +0x808
encoding/binary.(*encoder).value(0xc8200475a0, 0x162ba0, 0xc8204ac000, 0x59)
/usr/local/go/src/encoding/binary/binary.go:518 +0xb4f
encoding/binary.Write(0x621138, 0xc8200a2000, 0x621160, 0x289ef0, 0x162ba0, 0xc8204ac000, 0x0, 0x0)
/usr/local/go/src/encoding/binary/binary.go:316 +0x1792
main.listener(0x0, 0x1, 0xc82002a020)
/Users/ple/dev/go/src/Peter-test/main.go:59 +0xac8
created by main.main
/Users/ple/dev/go/src/Peter-test/main.go:105 +0x332